# Agent types Source: https://docs.quickblox.com/ai-agent/agent-types Compare QuickBlox AI Agent types — Knowledge Agent and Workflow Agent — and choose the right one for your use case QuickBlox AI Agent comes in two types: a **Knowledge Agent** for open-ended AI conversations, and a **Workflow Agent** for structured, designed flows. This page explains how each type works, how to choose between them, and which settings they share. ## Knowledge Agent vs Workflow Agent | | Knowledge Agent | Workflow Agent | | ----------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | | **How it answers** | Freely, drawing on its instructions and knowledge base | Follows a designed workflow graph | | **Conversation structure** | Open-ended — the user drives the conversation | Structured — the agent guides the user through defined steps | | **Knowledge base** | Yes — used for all answers | Yes — available via the AI response (RAG) node and as a fallback option | | **Workflows** | Not available | Yes — designed and managed on the Chat Workflows tab | | **Ideal for** | General Q\&A, support bots, FAQ agents | Lead capture, booking flows, structured support triage, multi-step data collection | | **Requires advanced feature** | No | Yes | ## Choose an agent type When you create a new AI Agent, you are asked to choose the agent type. 1. Go to ***Dashboard => YOUR\_APP => AI Agent***. 2. Click **Create**. 3. Under **Agent type**, select **Knowledge Agent** or **Workflow Agent**. 4. Fill in the remaining details and click **Create**. Choose AI Agent type The agent type cannot be changed after the agent is created. If you need a different type, create a new agent. ## Knowledge Agent A Knowledge Agent answers freely, drawing on its **Instructions** and its **Knowledge Base**. The user drives the conversation, and the agent responds to each message in turn — there is no predefined path. This is the default, simpler agent type and does not require the advanced feature. It is a good fit for: * General Q\&A and FAQ-style assistants * Support bots that answer from uploaded documentation * Conversations where flexibility matters more than a fixed sequence of steps Most of what shapes a Knowledge Agent lives in two places: * **Instructions** — the prompts that guide tone, behaviour, and how the agent answers. * **Knowledge Base** — the files, websites, and pages the agent draws on for retrieval-augmented (RAG) answers. See [Knowledge base](/ai-agent/knowledge-base). You can still collect information up front with an [Intake form](/ai-agent/intake-forms) before the open-ended conversation begins. ## Workflow Agent A Workflow Agent follows a designed **workflow** — a visual graph of steps that controls exactly what the agent says, what it collects, which systems it calls, and when the conversation ends. This type requires the advanced feature. Workflows are created and managed on the **Chat Workflows** tab. One agent can have many workflows, but only **one workflow can be active** for live conversations at a time. Chat Workflows Tab To learn how workflows work and how to build one, see: * [Workflows overview](/ai-agent/workflows/overview) — what a workflow is and when to use one * [Building a workflow](/ai-agent/workflows/editor) — create, publish, and activate a workflow Two behaviours are worth knowing when choosing this type: * **After a workflow ends**, each End node decides what happens next — continue with AI answering, answer from the knowledge base, or close the conversation. See [Workflow nodes](/ai-agent/workflows/nodes#after-workflow-ends). * **When no workflow is active** — for example, right after creating the agent or after deactivating every workflow — the agent falls back to standard AI answering using its instructions and knowledge base, behaving like a Knowledge Agent. See [Workflows overview](/ai-agent/workflows/overview#where-workflows-live). ## Settings shared by both agent types Both agent types share the same core configuration. A Workflow Agent simply adds the **Chat Workflows** tab on top. These settings apply regardless of the type you choose: * **Profile** — agent name, avatar, command, and where the agent responds. * **Knowledge Base** — content used for RAG answers (and by the AI response node and "Answer with Knowledge base" end action in workflows). * **Instructions** — the prompts for AI answering. The OpenAI prompt drives free-form answers (including the "Answer with AI" end action and fallback answering); the Knowledge base prompt drives knowledge-base (RAG) answers. (The OpenAI Chat workflow node uses its own per-node instructions instead.) * **Widget** — appearance and behaviour of the embedded chat widget. * **Human Handover** — configuration for the Live Chat node and the handover experience. Keep your instructions and knowledge base up to date for both agent types. For Knowledge Agents they drive every answer; for Workflow Agents they power fallback AI answering and the AI response node. ## Next steps For Knowledge Agents: * [Knowledge base](/ai-agent/knowledge-base) — add files, websites, and pages the agent answers from * [Intake forms](/ai-agent/intake-forms) — collect information before the conversation starts * [How to use](/ai-agent/how-to-use) — interact with your AI Agent in private and group chats For Workflow Agents: * [Workflows overview](/ai-agent/workflows/overview) — understand what a workflow is and when to use one * [Building a workflow](/ai-agent/workflows/editor) — create, publish, and activate a workflow * [Workflow nodes](/ai-agent/workflows/nodes) — reference of every available node type * [Variables, branching & testing](/ai-agent/workflows/variables-and-testing) — personalise flows and test before going live # AI Extensions Source: https://docs.quickblox.com/ai-agent/ai-extensions The AI Extensions module allows you to offer your users the latest and most diverse functionality almost effortlessly using QuickBlox API. ai-extensions.png The following AI extensions are currently available to you: ## AI Answer Assist The extension allows you to communicate directly with your AI Agent and use it as an assistant in conversations, where by passing the history of the conversation to it as context, it will be able to offer you an answer. [API documentation](/reference/ai-extensions-ai-answer-assist) ## AI Translate This extension will allow you to eliminate language barriers in your communication application as it supports a large number of languages. [API documentation](/reference/ai-extensions-ai-translate) ## AI Summarize This extension will generate a summary of your dialog. [API documentation](/reference/ai-extensions-ai-summarize) Please note that the summary will be generated using the last 1000 messages. ## HIPAA AI Gateway To obtain the HIPAA AI Gateway feature please contact us at [enterprise@quickblox.com](mailto:enterprise@quickblox.com). Using this extension you can send messages directly to OpenAI. You will need to pass your own system message, message history, etc. [API documentation](/reference/ai-extensions-ai-gateway) # How to use Source: https://docs.quickblox.com/ai-agent/how-to-use Here you will find how to use AI Agent in your application Since AI Agent is connected to a QuickBlox user, you can interact with it just like you would with a regular user. You have the option to create **private** or **group** dialogs with the AI Agent. AI Agent is not available in Public Dialogs. ## Create a private dialog with AI Agent The easiest way to interact with the AI Agent is to create a **private** (1-to-1) dialog — the AI Agent will answer any message you send. ### iOS To create a **private** (1-to-1) dialog, you need to set the dialog **type** field to `private` and provide the ID of the opponent you want to create a chat with. ```swift theme={null} let assistantId = 34 let dialog = QBChatDialog(dialogID: nil, type: .private) dialog.occupantIDs = [assistantId] QBRequest.createDialog(dialog, successBlock: { (response, createdDialog) in }, errorBlock: { (response) in }) ``` ```Objective-C theme={null} NSNumber *assistantId = @(34); QBChatDialog *dialog = [[QBChatDialog alloc] initWithDialogID:nil type:QBChatDialogTypePrivate]; dialog.occupantIDs = @[assistantId]; [QBRequest createDialog:dialog successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull createdDialog) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` ### Android To create a **private** (1-to-1) dialog, you need to set the dialog **type** to `QBDialogType.PRIVATE` and provide the ID of the opponent you want to create a chat with. ```java theme={null} int assistantId = 34; ArrayList occupantIdsList = new ArrayList(); occupantIdsList.add(assistantId); QBChatDialog dialog = new QBChatDialog(); dialog.setType(QBDialogType.PRIVATE); dialog.setOccupantsIds(occupantIdsList); QBRestChatService.createChatDialog(dialog).performAsync(new QBEntityCallback() { @Override public void onSuccess(QBChatDialog result, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` ```kotlin theme={null} val assistantId = 34 val occupantIdsList = ArrayList() occupantIdsList.add(assistantId) val dialog = QBChatDialog() dialog.type = QBDialogType.PRIVATE dialog.setOccupantsIds(occupantIdsList) // or just use DialogUtils // QBChatDialog dialog = DialogUtils.buildPrivateDialog(recipientId); QBRestChatService.createChatDialog(dialog).performAsync(object : QBEntityCallback { override fun onSuccess(result: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` ### JavaScript To create a **private** (1-to-1) dialog, you need to set the dialog **type** to `3` and provide the ID of the opponent you want to create a chat with. ```javascript theme={null} const assistantId = 34 const params = { type: 3, occupants_ids: [assistantId] }; QB.chat.dialog.create(params, function(error, dialog) {}); ``` ### ReactNative To create a **private** (1-to-1) dialog, you need to set the **type** to `QB.chat.DIALOG_TYPE.CHAT` and provide the ID of the opponent you want to create a chat with. ```javascript theme={null} const assistantId = 34 const params = { type: QB.chat.DIALOG_TYPE.CHAT, occupantsIds: [assistantId] }; QB.chat .createDialog(params) .then(function (dialog) { }) .catch(function (e) { }); ``` ### Flutter To create a **private** (1-to-1) dialog, you need to set the **type** to `QBChatDialogTypes.CHAT` and provide the ID of the opponent you want to create a chat with. ```Dart theme={null} int assistantId = 34; List occupantsIds = [assistantId]; int dialogType = QBChatDialogTypes.CHAT; try { QBDialog? createdDialog = await QB.chat.createDialog(occupantsIds, null, dialogType: dialogType); } on PlatformException catch (e) { } ``` ## Create a group dialog with AI Agent Your users can also interact with the AI Agent in **group** dialogs. ### iOS To create a **group** dialog for a predefined number of occupants, you need to set the dialog **type** field to `group` and provide the IDs of the opponents you want to create a chat with. ```swift theme={null} let assistantId = 34 let chatDialog = QBChatDialog(dialogID: nil, type: .group) chatDialog.name = "Dialog with assistant" chatDialog.occupantIDs = [assistantId, 45, 55] QBRequest.createDialog(chatDialog, successBlock: { (response, dialog) in dialog.join(completionBlock: { (error) in }) }, errorBlock: { (response) in }) ``` ```C theme={null} NSNumber *assistantId = @(34); QBChatDialog *chatDialog = [[QBChatDialog alloc] initWithDialogID:nil type:QBChatDialogTypeGroup]; chatDialog.name = @"Dialog with assistant"; chatDialog.occupantIDs = @[assistantId, @45, @55]; [QBRequest createDialog:chatDialog successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull dialog) { [dialog joinWithCompletionBlock:^(NSError * _Nullable error) { }]; } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` ### Android To create a **group** dialog for a predefined number of occupants, you need to set the dialog **type** to `QBDialogType.GROUP` and provide the IDs of the opponents you want to create a chat with. ```java theme={null} int assistantId = 34; ArrayList occupantIdsList = new ArrayList(); occupantIdsList.add(assistantId); occupantIdsList.add(45); occupantIdsList.add(55); QBChatDialog dialog = new QBChatDialog(); dialog.setName("Dialog with assistant"); dialog.setType(QBDialogType.GROUP); dialog.setOccupantsIds(occupantIdsList); QBRestChatService.createChatDialog(dialog).performAsync(new QBEntityCallback() { @Override public void onSuccess(QBChatDialog result, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` ```kotlin theme={null} val assistantId = 34 val occupantIdsList = ArrayList() occupantIdsList.add(assistantId) occupantIdsList.add(45) occupantIdsList.add(55) val dialog = QBChatDialog() dialog.name = "Dialog with assistant" dialog.type = QBDialogType.GROUP dialog.setOccupantsIds(occupantIdsList) QBRestChatService.createChatDialog(dialog).performAsync(object : QBEntityCallback { override fun onSuccess(result: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` ### JavaScript To create a **group** dialog for a predefined number of occupants, you need to set the dialog **type** to `2` and provide the IDs of the opponents you want to create a chat with. ```JavaScript theme={null} const assistantId = 34; const params = { type: 2, occupants_ids: [assistantId, 45, 55], name: "Dialog with assistant" }; QB.chat.dialog.create(params, function(error, dialog) {}); ``` ### ReactNative To create a **group** dialog for a predefined number of occupants, you need to set the dialog **type** to `QB.chat.DIALOG_TYPE.GROUP_CHAT` and provide the IDs of the opponents you want to create a chat with. ```JavaScript theme={null} const assistantId = 34; const params = { type: QB.chat.DIALOG_TYPE.GROUP_CHAT, name: 'Dialog with assistant', occupantsIds: [assistantId, 45, 55] }; QB.chat .createDialog(params) .then(function (dialog) { }) .catch(function (e) { }); ``` ### Flutter To create a **group** dialog for a predefined number of occupants, you need to set the dialog **type** to `QBChatDialogTypes.GROUP_CHAT` and provide the IDs of the opponents you want to create a chat with. ```Dart theme={null} int assistantId = 34; List occupantIds = [assistantId, 45, 55]; String dialogName = "Dialog with assistant"; int dialogType = QBChatDialogTypes.GROUP_CHAT; try { QBDialog? createdDialog = await QB.chat.createDialog(occupantIds, dialogName, dialogType: dialogType); } on PlatformException catch (e) { } ``` ## Send message to AI Agent in a group dialog with command To be able to interact with an AI Agent in a **group** dialog the following conditions must be met: * **Enable** AI Agent for **group** dialogs on the [dashboard](/ai-agent/manage-agents#create-ai-agent). * Define the **command** for the AI Agent on the [dashboard](/ai-agent/manage-agents#create-ai-agent). * The AI Agent **must** be added to the **group** dialog and be present in the `occupant_ids`. * Your message **must** begin with a **command**. For example, if you defined your AI Agent **command** as "SuperHelpfulAssistant", your message may look like: `/SuperHelpfulAssistant Help me with something` ## Send message to AI Agent in a group dialog with mentions To be able to interact with an AI Agent in a **group** dialog via mentions, the following conditions must be met: * **Enable** AI Agent for **group** dialogs on the [dashboard](/ai-agent/manage-agents#create-ai-agent). * The AI Agent **must** be added to the **group** dialog and be present in the `occupant_ids`. * The message **must** contain the AI Agent's user ID in the `mentioned_user_ids` custom field. If you want to mention several users, you must enumerate them joined by a comma (,). Example: "34,45,55" ### iOS ```swift theme={null} let message = QBChatMessage() message.text = "Hi" message.customParameters["save_to_history"] = true message.customParameters["mentioned_user_ids"] = "34" let dialog = ... dialog.send(message) { (error) in } ``` ```objective-c theme={null} QBChatMessage *message = [[QBChatMessage alloc] init]; message.text = @"Hi"; message.customParameters[@"save_to_history"] = @"1"; message.customParameters[@"mentioned_user_ids"] = @"34"; QBChatDialog *dialog = ...; [dialog sendMessage:message completionBlock:^(NSError * _Nullable error) { }]; ``` ### Android ```java theme={null} QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setSaveToHistory(true); chatMessage.setBody("Hi"); chatMessage.setProperty("mentioned_user_ids", "34"); qbChatDialog.sendMessage(chatMessage, new QBEntityCallback() { @Override public void onSuccess(Void void, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` ```kotlin theme={null} val chatMessage = QBChatMessage() chatMessage.setSaveToHistory(true) chatMessage.body = "Hi" chatMessage.setProperty("mentioned_user_ids", "34") qbChatDialog.sendMessage(chatMessage, object : QBEntityCallback { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` ### JavaScript ```JavaScript theme={null} var message = { type: "groupchat", body: "Hi", extension: { save_to_history: 1, mentioned_user_ids: "34" }, }; try { QB.chat.send(jidOrUserId, message); } catch (e) { if (e.name === "ChatNotConnectedError") { } } ``` ### ReactNative ```JavaScript theme={null} const message = { dialogId: 'dsfsd934329hjhkda98793j2', body: 'H1', properties: { mentioned_user_ids: "34" }, saveToHistory: true }; QB.chat .sendMessage(message) .then(function () { }) .catch(function (e) { }) ``` ### Flutter ```Dart theme={null} String dialogId = "dsfs9344349hjkdsda9877932j2"; String body: 'Hi'; bool saveToHistory = true; Map properties = Map(); properties["mentioned_user_ids"] = "34"; try { await QB.chat.sendMessage(dialogId, body: body, saveToHistory: saveToHistory, properties: properties); } on PlatformException catch (e) { } ``` # Human handover Source: https://docs.quickblox.com/ai-agent/human-handover Learn how to manage Human handover If the AI Agent doesn't know the answer, it can forward the conversation to a human agent. ## Enable Human handover To turn on human handover: 1. Go to ***Dashboard => YOUR\_APP => AI Agent***. 2. Find the AI Agent you want to update and click on its ID. 3. Scroll down to the **Unanswered questions** section. 4. Check the **Human handover** option. Human handover Human handover 5. If needed, edit the **Message for human handover**. 6. You can also turn on **Notify user that agent is assigned to chat** and change the message if you want. 7. Choose whether dialogs should be assigned to online agents only, or both online and offline agents. You can also set a message for when an offline agent is assigned (this message is empty by default). 8. Customize **Message if there is no available agent** if needed. 9. To pick which agents can be assigned, click **Add**. 10. Select the agents you want to assign. If no agents appear, you need to create them first. 11. Click **Add** to confirm. Select agents 12. Save the AI Agent changes. Please note that only group dialogs can be handed over to agents. In a private dialog the user will receive the "don't know" message. # Intake forms Source: https://docs.quickblox.com/ai-agent/intake-forms Learn how to manage Intake forms Intake forms allow you to design a structured flow of questions that users must answer before they start interacting with the AI Agent or are transferred to a human agent. This feature helps gather important information upfront and improves the quality of the conversation that follows. To create an Intake form: 1. Navigate to the ***Dashboard => YOUR\_APP => AI Agent*** page. 2. Choose the AI Agent you want to edit or create a new one. 3. Scroll to the **Intake form** section. Intake forms 4. Check the **"Enable intake form"** checkbox to activate intake form functionality. 5. Click **Manage questions** and configure the questions you want to ask (for example, name, email, issue type). 6. Arrange the questions in the desired order. Add questions to intake form 7. Fill in optional fields: * **Role**: Define how the AI Agent should present itself during the intake. * **Greeting message**: Shown before the first question to provide a friendly or informative introduction. * **Ending message**: Displayed after the last question to wrap up the process. 8. Choose the actions to perform after the intake process is finished: * **Send a summary**: A short recap message will be posted in the chat. * **Enable the human handover**: Transfers the conversation to a live agent. * **Send Webhook**: Triggers a webhook with intake responses for backend processing.\ The webhook will be sent in the following format: ```json theme={null} { "dialog": { "id": "6788f7a357aa22e58d509b3a", "name": "GuestUser_q8V4GExUvvBu6cj7UCtXEGdGiR", "type": 2, "user_id": "205", "application_id": "1", "occupants_ids": [ "201", "205" ] }, "message": { "id": "6788f7aeb11e91aeba44718e", "type": "group", "sources": null, "body": "The person's name is Jim, and they like to play.", "to": "6788f7a357aa22e58d509b3a", "from": "201", "dialog_id": "6788f7a357aa22e58d509b3a", "application_id": "1", "date_sent": null, "extra_params": { "answer_0": "jim", "answer_1": "play", "intake_complete": 1, "question_0": "What is your name?", "question_1": "What do you like?", "questions_count": 2 }, "is_bot_message": 1, "is_widget?": null, "mentions": null, "suggested_questions": null, "unknown_answer": null } } ``` 9. Save the changes. # Knowledge base Source: https://docs.quickblox.com/ai-agent/knowledge-base Learn how to manage the AI Agent knowledge base ### Manage knowledge base content In this section, you can add websites, separate webpages, or upload files that will be used as knowledge base sources by your AI Agent. Manage knowledge base content screenshot #### Add websites To add a website to the AI Agent, follow these steps: 1. Go to ***Dashboard => YOUR\_APP => AI Agent***. 2. Find the AI Agent you want to update and click on its ID. 3. Scroll down to the **My content** section. 4. Enter the website URL. 5. Click **Add**. Add websites to AI Agent knowledge base 6. Select the pages you want to add, or click **Select all** to add every page. 7. To automatically add new pages from the website, check **Enable auto sync for website**. If you enable auto sync, **ALL** pages from the website will be added. Select webpages to add 8. Click **Save**. Please note that when you add a web page you are adding that particular web page and not the whole website. When you remove a website, all pages associated with that website will also be automatically removed. #### Add webpages To add a webpage to the AI Agent, follow these steps: 1. Go to ***Dashboard => YOUR\_APP => AI Agent***. 2. Find the AI Agent you want to update and click on its ID. 3. Scroll down to the **My content** section. 4. Click on the **Webpages** tab. 5. Enter the webpage URL (including https\:// or http\://). 6. Click **Add**. Add webpages to AI Agent knowledge base #### Add files To add a file to the AI Agent, follow these steps: 1. Go to ***Dashboard => YOUR\_APP => AI Agent***. 2. Find the AI Agent you want to update and click on its ID. 3. Scroll down to the **My content** section. 4. Click on the **Files** tab. 5. Click the **Browse** button and select a file (or drag and drop the file). Add files to AI Agent knowledge base You can upload .docx, .txt, and .pdf files with a max size of up to 25 MB. # Manage AI Agents Source: https://docs.quickblox.com/ai-agent/manage-agents Learn how to manage AI Agents ## View AI Agents To view a list of AI Agents, follow the steps below: 1. Navigate to the ***Dashboard => YOUR\_APP => AI Agent*** page. View AI Agents ## Create AI Agent To create an AI Agent, follow the steps below: 1. Navigate to the ***Dashboard => YOUR\_APP => AI Agent*** page. 2. Click the **Create** button. 3. Choose an agent type — **Knowledge Agent** or **Workflow Agent** — and click **Next**. (See [Agent Types](/ai-agent/agent-types) for the difference.) 4. Enter a **Name** for the agent and click **Create**. Choose AI Agent type Enter AI Agent name Create AI Agent The agent is created and opens on its edit page, where the rest of the settings live. You can change the name at any time, and a unique user (login) for the agent is generated automatically — you do not set a username. Agent type is chosen only at creation and cannot be changed afterwards. This page covers the settings shared by both agent types; for workflow-specific configuration, see [Workflows overview](/ai-agent/workflows/overview). ## Configure your AI Agent The agent edit page is organised into tabs, and each editable tab has its own **Save** button. ### Profile * **Name** — the name shown to users in chats. Can be changed anytime. * **Avatar** — upload an image (JPG, JPEG, or GIF) used as the agent's avatar. * **Enable AI Agent in chats** — choose where the agent responds using the **Private** and **Group** checkboxes. If it is disabled for a dialog type, the agent can still be added to a dialog but won't answer there. * **Command** — a single word that activates the agent in group chats. When users type `/command`, they can talk to the agent in that group (the agent's user must be in the chat too). The Profile tab also contains the **Delete AI Agent** action and, depending on your account, optional response settings. ### Knowledge Base On the **Knowledge Base** tab you choose where answers come from — **Your uploaded content** or **OpenAI** — and manage the content the agent can use. Under **Unanswered questions**, choose what happens when the agent can't answer from its knowledge base: | Option | Behaviour | | --------------------- | --------------------------------------------------------------------------------------------------------------------------- | | **Default answer** | Replies with the configured **"I don't know the answer"** message. | | **Forward to OpenAI** | Forwards the question to OpenAI so the user gets a general AI answer. | | **Send Webhook** | Forwards the question to your configured webhook URL. The agent does not post a reply itself — implement your own handling. | | **Human handover** | Forwards the conversation to a human agent. Use **Configure** to set this up on the **Human Handover** tab. | See [Knowledge Base](/ai-agent/knowledge-base) for full details on managing content. ### Instructions The **Instructions** tab holds two prompts that shape the agent's tone and behaviour: * **Knowledge base** — used when the knowledge base is set to **Your uploaded content**. * **OpenAI** — used when the knowledge base is set to **OpenAI**, and when unanswered questions are forwarded to OpenAI. ### Other tabs * **Human Handover** — configure transfer to human agents. See [Human handover](/ai-agent/human-handover). * **Intake Form** — collect details before answering. See [Intake forms](/ai-agent/intake-forms). * **Widget** — appearance and embedding of the chat widget. See [Widget](/ai-agent/widget). * **Analytics** — usage charts and an unanswered-questions report (read-only). ## Update AI Agent To update an AI Agent, follow the steps below: 1. Navigate to the ***Dashboard => YOUR\_APP => AI Agent*** page. 2. Click the AI Agent you want to update. 3. Open the relevant tab and edit its settings. 4. Click the **Save** button on that tab to save your changes. Edit AI Agent ## Delete AI Agent You can delete an AI Agent in two ways: * **From the list:** on the ***Dashboard => YOUR\_APP => AI Agent*** page, select the agents using their checkboxes and click **Remove selected**. * **From the agent:** open the agent and, on the **Profile** tab, click **Delete** in the **Delete AI Agent** section. Deleting an agent permanently removes it from your application. ## Test AI Agent Every agent edit page includes the **AI Agent Tester** on the right side, where you can chat with the agent to try out its behaviour. Some behaviours — such as Human handover — are only fully exercised in the widget or via SDKs. In the dashboard tester you may just receive the "I don't know the answer" message. # Overview Source: https://docs.quickblox.com/ai-agent/overview Learn about AI Agents Welcome to AI Agent! This AI-powered chat companion not only chats with your users but also learns from uploaded content like files and web pages. Here's what makes AI Agent special: * **Enhanced Knowledge Base:** AI Agent can use information from a variety of sources, including files and web pages you upload. This feature allows it to provide more accurate and relevant responses. * **Seamless User Interaction:** Linked directly to a QuickBlox user, the AI Agent can participate in both private and group chats, offering a personalized experience to each user. * **Tailored Chat Engagement:** With the AI Agent, you have the flexibility to choose its operational environments. Whether you want the AI Agent active in private chats, group chats, or both, you're in control. * **Flexible Behavior and Identity:** Shape the AI Agent's identity and guide its behavior to fit your app's tone and user needs. From casual to formal, it can adapt to the vibe you want. * **Advanced Options for Unanswered Queries:** Decide how the AI Agent should react when it encounters a question it doesn't know. You can set it to respond with a default message, consult OpenAI for a more advanced answer, forward to a human agent, or use a custom webhook for unique handling. # Widget Source: https://docs.quickblox.com/ai-agent/widget Learn how to manage Widget The widget lets users chat with the AI Agent on your website without writing any code. It's easy to add. How to add the widget to your website: 1. Go to ***Dashboard => YOUR\_APP => AI Agent***. 2. Find the AI Agent you want to update and click on its ID. 3. Scroll down to the **AI Agent widget** section. AI Agent widget section 4. Check the **"Add to your website"** checkbox. AI Agent widget section AI Agent widget section 5. Fill in the following details: * **Company name**. * **Company logo**. * **Widget color**. * **Widget button logo**. * **Domain**, if the widget should work only on a specific domain. 6. Copy the widget code from either: * **"Add to display within a webpage"**, or * **"Add as floating button, that can open/close"**, depending on how the widget should appear. 7. Paste the code into the webpage where the widget should be shown. 8. Save the changes to the AI Agent. # Building a workflow Source: https://docs.quickblox.com/ai-agent/workflows/editor Create, configure, publish, and activate a workflow in the visual editor The workflow editor lets you build a conversation flow by dragging nodes onto a canvas, connecting them, and configuring each step in a side panel. Changes are saved automatically as you work. ## Create a workflow 1. Go to ***Dashboard => YOUR\_APP => AI Agent***. 2. Open the Workflow Agent you want to add a workflow to. 3. Click the **Chat Workflows** tab. 4. Click **Create** (the button reads **Create Workflow** on the empty state). 5. Enter a name for the workflow and confirm. The workflow is created in draft state and appears in the workflows list. Click **Edit** next to it to open it in the editor. New Workflow ## Editor layout The editor has three main areas: | Area | What it does | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | **Canvas** (centre) | Your workflow graph. Drag nodes here, click a node to select it, and drag from a node's output port to connect it to the next node. | | **Node palette** (left) | All available node types organised into groups. Drag any node onto the canvas to add it. | | **Node Settings** (right) | Configuration panel for the currently selected node. Configure its message, options, variables, and behaviour here. | The editor auto-saves your changes. You do not need to click a Save button while editing — focus on building the flow. Workflow example ## Import and export You can share or back up a workflow as a JSON file. Both actions live in the editor toolbar, so open a workflow first. * To **export**: open the workflow and click the **Export** icon in the editor toolbar. The JSON file is downloaded to your device. * To **import**: open a workflow, click the **Import** icon in the editor toolbar, and select a previously exported JSON file. The imported graph replaces the canvas of the currently open workflow. Importing overwrites the currently open workflow's canvas. The change is auto-saved as the draft working copy — review and publish it before activating. ## Publish a workflow Publishing makes a workflow available to be activated for live conversations. Before you can publish, every node must meet the following requirements: * Every node must be **connected** — no isolated nodes on the canvas. * Every node must be **reachable** — each node needs an incoming connection, with no orphaned nodes that nothing links to. * Every path must ultimately reach an **End** node — no open output ports. If any requirement is not met, the editor highlights the problem and prevents publishing. To publish a workflow: 1. Finish building and configuring all nodes. 2. Click **Publish** in the editor toolbar. 3. Resolve any validation errors shown, then click **Publish** again. Workflow error When you edit a published workflow, your changes are auto-saved to a draft working copy. Your live agent keeps using the previously published version until you click **Publish** again. If the workflow is already active, you do not need to reactivate it after re-publishing. ## Activate a workflow Only one workflow per agent can be active at a time. Activating a workflow makes it the one that runs for all new conversations on that agent. 1. Go to the **Chat Workflows** tab of the Workflow Agent. 2. Find the published workflow you want to make live. 3. Toggle the **Status** switch to **Active**. The previously active workflow (if any) is automatically deactivated. Several Workflows Publish your workflow before activating it. Activating a workflow that has never been published leaves live conversations with no flow to run. ## Preview and test You can test your workflow without affecting live users. * Click **Preview** in the editor toolbar to open the embedded chat widget and interact with the workflow as a user would. * Use the **AI Agent Tester** (available from the agent's main settings) to run through the flow and inspect how each node responds. Preview Workflows Live Chat handover nodes are simulated in preview — no real agent is notified. See [Variables, branching & testing](/ai-agent/workflows/variables-and-testing) for full testing guidance. # Workflow nodes Source: https://docs.quickblox.com/ai-agent/workflows/nodes Reference of every workflow node type and when to use it Nodes are the building blocks of a workflow. Each node represents one step in the conversation. You add nodes by dragging them from the node palette onto the canvas, then configure them in the Node Settings panel. Nodes are organised into four groups in the UI: **Send**, **Request**, **Smart Actions**, and the fixed **Start / End** pair. Node palette *** ## Send nodes Send nodes deliver content to the user without expecting a reply. | Node | Description | When to use | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | **Message** | Sends a predefined text message to the user. Supports variable substitution so you can personalise the text with previously collected answers. | Greetings, instructions, confirmations, or any static text you want the agent to display. | | **Web link** | Sends a clickable link with optional label text. | Directing users to a webpage, documentation, or an external resource. | *** ## Request nodes Request nodes ask the user for information and wait for a response. Each answer can be saved as a named variable for use later in the flow. | Node | Description | When to use | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | **Name** | Asks for the user's name. | Personalising the rest of the conversation. | | **Phone number** | Asks for a phone number, with optional format validation. | Collecting contact details. | | **Email** | Asks for an email address, with optional format validation. | Collecting contact details or account lookup. | | **Text question** | Asks a free-text question and accepts any reply. | Open-ended responses where the exact format does not matter. | | **Numeric input** | Asks for a number. | Order quantities, reference numbers, ages, or any numeric value. | | **Date/time** | Asks for a date and/or time. | Scheduling, booking, or filtering by date. | | **File Upload** | Prompts the user to upload a file. | Support tickets, document submissions, or photo evidence. | | **Single Choice** | Presents a set of options and accepts exactly one selection. | Menus, categories, or yes/no questions. | | **Multiple Choice** | Presents a set of options and accepts one or more selections. | Preferences, multi-select filters, or feature requests. | | **Smart Question** | Matches the user's free-text reply against keywords you define for each intent (using **Contain** or **Exact** matching) and routes to that intent's path. A fallback path handles replies that match no keyword. | Routing users based on what they type rather than a button tap — for example, detecting "billing", "technical issue", or "account" from keywords. | *** ## Smart Actions nodes Smart Actions perform logic, AI operations, or integrations. | Node | Description | When to use | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **OpenAI Chat** | Starts a multi-turn AI conversation within the workflow using the instructions you set on the node. The user can chat back and forth with the AI; the flow moves on to the next node once the configured turn limit (**Max Turns**) is reached. | Conversational sub-flows where you want flexible AI interaction at a specific point in an otherwise structured flow. | | **AI response (RAG)** | Answers the user's question using the agent's knowledge base. You can optionally enable a fallback — a "don't know" message and/or a fallback node — for when no relevant answer is found. | Knowledge base look-ups at a specific step in the flow; especially useful when you want AI answers only after collecting required context. | | **If / Else** | Evaluates one or more conditions based on variable values or the previous node's output, then routes the conversation to the matching branch. | Branching the flow based on what the user has already answered — for example, routing VIP customers differently, or skipping steps based on earlier selections. | | **Webhook** | Sends an HTTP request to an external URL with a method, headers, and body you define (any of which can include collected variables). You must define a **fallback path** for when the webhook call fails. | CRM updates, booking confirmations, look-ups in external systems, or any integration requiring a server-side call. | | **Live Chat** | Transfers the conversation to a human agent. The workflow pauses while the human handles the conversation. | Escalation scenarios where the issue requires human attention — for example, after a complaint or when the AI cannot resolve the request. | *** ## Start and End nodes These nodes are fixed anchor points in every workflow. | Node | Description | | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Start** | The single entry point of every workflow. Every conversation begins here. It cannot be deleted or duplicated. | | **End** | Marks the terminal point of a conversation path. You can have multiple End nodes for different paths. Each End node has an **After workflow ends** setting (see below) and an optional farewell message. | ### After workflow ends Each **End** node has an **After workflow ends** setting that controls what the AI Agent does once a conversation path reaches that End node. | Setting | Behaviour | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | | **Answer with AI** | The agent continues the conversation using its OpenAI instructions — free-form AI answering, as a Knowledge Agent would. | | **Answer with Knowledge base** | The agent switches to knowledge base (RAG) answering, responding only from uploaded content. | | **End Conversation** | The conversation is closed. No further AI responses are sent. | You can set different behaviours on different End nodes within the same workflow — for example, ending the conversation after a successful booking but falling back to AI answering after an informational path. End Node Options # Workflows overview Source: https://docs.quickblox.com/ai-agent/workflows/overview Learn what workflows are and how they power advanced AI Agents A workflow is a visual conversation flow that defines exactly how your AI Agent interacts with users — step by step. Instead of relying on open-ended AI responses alone, you design a graph of nodes (steps) connected by paths, giving you full control over what the agent says, what information it collects, which external systems it calls, and when the conversation ends. Workflows are the foundation of the **Workflow Agent** type — the advanced agent that follows a designed flow. If you want a simpler **Knowledge Agent** that answers freely from its knowledge base and instructions, see [Agent Types](/ai-agent/agent-types) for a comparison of the two agent types. ## What a workflow looks like A workflow is a directed graph: * **Nodes** are the individual steps — sending a message, asking a question, branching on a condition, calling a webhook, and more. * **Edges** are the paths that connect nodes, forming the conversation flow from start to finish. * Every workflow has exactly one **Start** node (where every conversation begins) and one or more **End** nodes (where a conversation path concludes). ## Where workflows live Workflows are attached to a **Workflow Agent**. You can find them on the **Chat Workflows** tab when you open a Workflow Agent in the dashboard. One agent can have many workflows, but only **one workflow can be active** for live conversations at a time. Activating a workflow automatically deactivates the previously active one; every other workflow stays inactive (whether draft or published) until you activate it. If a Workflow Agent has **no active workflow** — for example, immediately after creation or after deactivating all workflows — it falls back to standard AI answering using its instructions and knowledge base, behaving like a Knowledge Agent. Workflows list ## Next steps * [Agent Types](/ai-agent/agent-types) — understand agent types and how to enable the advanced feature * [Building a workflow](/ai-agent/workflows/editor) — create, configure, publish, and activate a workflow * [Workflow nodes](/ai-agent/workflows/nodes) — reference of every node type and what it does * [Variables, branching & testing](/ai-agent/workflows/variables-and-testing) — save answers, build conditions, and test your flow # Variables, branching & testing Source: https://docs.quickblox.com/ai-agent/workflows/variables-and-testing Save user answers as variables, build conditional branches, and test your workflow Variables let you capture what users say and carry that information forward through the flow. Combined with branching nodes, they allow your workflow to adapt to each user's specific situation. This page also covers how to test your workflow safely before it reaches live users. ## Variables Every **Request** node has an optional **Variable** field. When you enter a name there, the user's answer is stored under that name for the rest of the workflow run. ### How to name variables * Use short, descriptive names without spaces (for example, `user_name`, `issue_type`, `order_number`). * Variable names are case-sensitive. * You can define as many variables as you need across different Request nodes. ### How to use variables Once saved, you can reference a variable in: * **Message nodes** — personalise text by inserting the variable (for example, `Thanks, {{vars.user_name}}! Let me look into that for you.`). * **Webhook nodes** — include variable values in the request URL, headers, or payload sent to your external system. * **OpenAI Chat nodes** — pass variable values into the node's initial context. * **If / Else conditions** — reference variable values in the condition expression to decide which branch to follow. Reference variables using double curly braces with the `vars.` prefix: `{{vars.variable_name}}`. The variable is replaced with the user's actual answer at runtime. (Plain `{{variable_name}}` without the prefix is not substituted.) ### System variables Alongside the variables you collect, every run carries a set of **read-only system variables** that describe the conversation context. Reference them with the `system.` prefix — the same way you reference `vars.`, but you never set or change them. | Variable | Type | Description | | ----------------------------- | ------ | ----------------------------------------------------------- | | `{{system.application_id}}` | number | The QuickBlox application the conversation belongs to. | | `{{system.ai_agent_id}}` | string | The AI Agent running the workflow. | | `{{system.dialog_id}}` | string | The chat dialog the conversation is taking place in. | | `{{system.ai_agent_user_id}}` | number | The AI Agent's bot user id. May be absent in some contexts. | System variables are available everywhere `vars` are: Message node text, Webhook URL/headers/body, OpenAI Chat context, and If / Else conditions (as `system.application_id`, and so on). ```jsonc theme={null} // Example Webhook body using both namespaces { "application": "{{system.application_id}}", "dialog": "{{system.dialog_id}}", "issue": "{{vars.issue_type}}" } ``` ### Save variables from an OpenAI Chat node The **OpenAI Chat** node can save values to `vars` directly — but only if you tell it to in the node's **Instructions**. The node gives the model a built-in tool for storing workflow variables, and the model uses it whenever your instructions ask it to capture something. Write the instruction in plain language naming the variable, for example: ```text theme={null} Ask the user what product they are calling about. When they answer, save their response in a variable named "product". If they mention an order number, save it in a variable named "order_number". ``` When the model captures those values, they are written to `vars` immediately and persist for the rest of the run — exactly like a Request node's answer. You can then reference them downstream with `{{vars.product}}` in a Message or Webhook, or branch on `vars.order_number` in an If / Else. Variable names follow the same rules as elsewhere — short, descriptive, case-sensitive, no spaces. Names starting with `__` are reserved and rejected. Stored values can be a string, number, boolean, a list of those, or a flat object of those. The `set_variable` tool is available only on the **OpenAI Chat** node — it lets the model decide *what* to capture from the conversation. To save the node's *result* instead, use the **Output variable** field described next (available on both the OpenAI Chat and AI response nodes). ### Save the node result with Output variable Both the **OpenAI Chat** and **AI response (RAG)** nodes have an **Output variable** field. When you set it, the node's completion result is saved to that variable, and you can read it downstream with `{{vars.*}}` or branch on it in an If / Else. The stored value is the node's full result object, so reference nested fields with dot notation: * **AI response (RAG)** — when the knowledge base cannot answer, the result is `{ status: "unknown_answer", last_reply: "..." }`. With **Output variable** set to `kb_answer`, you can use `{{vars.kb_answer.last_reply}}` or branch on `vars.kb_answer.status == "unknown_answer"`. * **OpenAI Chat** — when the conversation reaches its **Max Turns** limit, the result is `{ status: "limit_reached", turns_used, last_reply: "..." }`. With **Output variable** set to `ai_chat`, you can use `{{vars.ai_chat.last_reply}}`. Use **Output variable** when you want the node's *final reply or status* available later in the flow or in a Message/Webhook template. Use an OpenAI Chat **save instruction** (`set_variable`) when you want the model to extract a *specific piece of information* from what the user said. ### Reference the output of the previous node When a node's result is not stored in a named variable, you can still react to it on the **conditions of the edges leaving that node** via `last_node.output`. This is handy for branching on an **AI response (RAG)** or **OpenAI Chat** node's result right after it runs, without setting an Output variable. `last_node.output` is only available in **If / Else and edge conditions (CEL)** — not in `{{...}}` message or webhook templates. It always reflects the node that *just* completed and is overwritten by the next node, so branch on it immediately after the node. **AI response (RAG) node** — when the knowledge base cannot answer (and a fallback is configured), the node completes with: ```jsonc theme={null} { "status": "unknown_answer", // the model could not answer from the knowledge base "last_reply": "..." // the reply text (or your "don't know" message) } ``` Branch on it from the RAG node's outgoing edge, for example: ```text theme={null} last_node.output.status == "unknown_answer" ``` **OpenAI Chat node** — when the conversation reaches its **Max Turns** limit, the node completes with: ```jsonc theme={null} { "status": "limit_reached", "turns_used": 5, "last_reply": "..." // the assistant's final reply } ``` ```text theme={null} last_node.output.last_reply.contains("refund") ``` `last_node.output` lives only on the edges directly leaving the node and is overwritten by the next node, so you cannot reference it later in the flow or inside a `{{...}}` Message/Webhook template. If you need a value downstream, save it to a variable instead — with a Request node, a Webhook or agentic node **Output variable**, or an OpenAI Chat **save** instruction (see above) — and then read it as `{{vars.*}}`. *** ## Branching Branching lets your workflow follow different paths based on what users have answered. There are two ways to branch. ### If / Else node The **If / Else** node evaluates one or more conditions in sequence: 1. Add a **Condition** and give it a **Label** plus an **Expression (CEL)** — for example, `vars.issue_type == "billing"`. 2. Click **Add Condition** for each additional branch you need. 3. Set a **Default branch (else)** as the path to follow when no condition matches. Conditions are written as [CEL](https://github.com/google/cel-spec) expressions. Common operators include `==` (equals), `!=` (not equals), `>`, `>=`, `<`, and `<=` for numeric comparisons, `&&` / `||` to combine checks, and string helpers such as `.contains("...")` and `.matches("...")`. Conditions can reference saved answers with the `vars.` prefix (for example, `vars.order_number > 1000`), run context with the `system.` prefix (for example, `system.application_id`), and the previous node's result with `last_node.output` (for example, `last_node.output.status == "unknown_answer"` right after an AI response node). ### Conditional connections on Request nodes **Single Choice** and **Smart Question** nodes let you connect a specific path for each answer option (or detected intent). You can route users directly to the right next step based on their selection without an extra If / Else node. **Multiple Choice** nodes have a single onward path plus a fallback for invalid selections, so route their results with an If / Else node when you need to branch. ### Branch order matters Conditions are evaluated top to bottom. Place your most specific conditions first and the default (Else) branch last. Always connect an Else or default path. If no condition matches and there is no default, the workflow has an unresolved state. *** ## Testing ### Widget Preview The fastest way to test is the **Preview** button in the editor toolbar. It opens an embedded chat widget so you can walk through the flow as a user would — entering answers, triggering branches, and seeing messages — without affecting live users. ### AI Agent Tester The **AI Agent Tester** is available from the agent's main settings page. It runs the full agent logic, including the active workflow, in an isolated test environment. Use it to: * Test edge cases and uncommon paths. * Verify that variables are saved and referenced correctly. * Confirm that webhook calls are being triggered with the right data. Preview Workflows Live Chat handover is simulated in both Preview and the AI Agent Tester. The conversation will show a handover message, but no real human agent is notified during testing. *** ## Best practices ### Connect every path to an End node Before publishing, confirm that every output port of every node leads somewhere — either to the next node or to an End node. Unconnected paths fail the publish validation check. ### Always set a webhook fallback The **Webhook** node requires a fallback path. If the external server is unavailable or returns an error, the conversation follows the fallback path instead of stalling. Design the fallback to handle the situation gracefully — for example, an apology message followed by an End node or a Live Chat handover. ### Order branches from specific to general In If / Else nodes, put the most specific conditions at the top and the broadest default (Else) at the bottom. This prevents a general condition from matching before a more specific one gets evaluated. ### Use descriptive variable names Choose variable names that make your conditions readable (for example, `subscription_type`, not `var1`). This is especially helpful when collaborating with teammates or returning to a workflow after time away. *** ## Caveats | Behaviour | Detail | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Webhook failures** | If a Webhook node cannot reach the external URL or receives an error response, the conversation follows the configured fallback path. Check your webhook URL and server health if users report unexpected fallback behaviour. | | **Live Chat in preview** | Live Chat handover is simulated in Preview and the AI Agent Tester. No live agent receives a notification. Test real handover behaviour in a staging environment with actual agent accounts. | | **Draft vs published** | Editing a published workflow immediately returns it to draft. The previously published version remains live until you re-publish and, if necessary, reactivate the workflow. | # Chat Samples Source: https://docs.quickblox.com/code-samples/chat-samples Simple chat samples with all the features essential for a seamless and up-to-date messaging experience based on QuickBlox API. **Features** * Login/logout * Send and receive message/attachment * Create and leave a 1-to-1 and group chat * Create a public chat * Display users who have received/read the message * Mark messages as read/delivered * Send typing indicators * List and delete chats * Display chat history * Display a list with chat participants * Send/receive push notifications * Subscribe/unsubscribe device to push notifications Choose the code sample below and head to GitHub repositories to explore and clone it. Go over the relevant documentation for more details. View on GitHub
Documentation
View on GitHub
Documentation
View on GitHub
Documentation
} > View on GitHub
Documentation
View on GitHub
Documentation
View on GitHub
Documentation
View on GitHub
Documentation
Apache Cordova } > View on GitHub
Documentation
Live Demo
View on GitHub
Documentation
# Chat UIKit samples Source: https://docs.quickblox.com/code-samples/chat-ui-kit-samples Simple chat samples with all the features essential for a seamless and up-to-date messaging experience based on QuickBlox API. **Features** * SignUp/Login/logout * Change a Color Theme * Send and receive message/attachment * Create and leave a 1-to-1 and group chat * Create a public chat * Display users who have received/read the message * Mark messages as read/delivered * Send typing indicators * List and delete chats * Display chat history * Display a list with chat participants Choose the code sample below and head to GitHub repositories to explore and clone it. Go over the relevant documentation for more details. View on GitHub
Documentation
Live Demo
View on GitHub
Documentation
View on GitHub
Documentation
# Chatbot samples Source: https://docs.quickblox.com/code-samples/chatbot-samples Choose the code sample below and head to GitHub repositories to explore and clone it. Go over the relevant documentation for more details. View on GitHub
Documentation
View on GitHub
Documentation
# Overview Source: https://docs.quickblox.com/code-samples/overview This page displays a collection of QuickBlox code samples in different programming languages. # Push notification samples Source: https://docs.quickblox.com/code-samples/push-notification-samples Developer-friendly push notifications samples that allow you to immediately deliver some information to a user's device when the device is either idle or running the app in the background, built for web and mobile platforms. **Features** * Send instant notifications * Send recurring notifications * Send scheduled notifications * Subscribe a device for APNs and VoIP push notifications * Send a text push notification to a user * Display a list of received push notifications Choose the code sample below and head to GitHub repositories to explore and clone it. Go over the relevant documentation for more details. View on Gihtub
Documentation
View on GitHub
Documentation
View on GitHub
Documentation
} > View on GitHub
Documentation
# Video calling samples Source: https://docs.quickblox.com/code-samples/video-calling-samples Feature-rich video calling samples that allow you to make high-quality voice and video calls, built for web and mobile platforms. **Features** * Login/logout * Make and receive a 1-to-1 and group audio call * Make and receive a 1-to-1 and group video call * Search for users to make a call with * Mute/unmute the microphone * Display the list of call participants and their statuses * Share a screen * Switch camera * Display call stats * Switch speaker Choose the code sample below and head to GitHub repositories to explore and clone it. Go over the relevant documentation for more details. View on GitHub
Documentation
View on GitHub
Documentation
View on GitHub
Documentation
} > View on GitHub
Documentation
View on GitHub
Documentation
View on GitHub
Documentation
Live Demo
View on GitHub
Documentation
# Video conference samples Source: https://docs.quickblox.com/code-samples/video-conference-samples Simple and feature-rich video conference samples that enable you to easily implement a conference with more than ten people, build for web and mobile platforms. **Features** * Login/logout * Create a conference room * Leave a conference room * Add a new member to a conference room * Participate in a conference call as a streamer/watcher * Send and receive text messages and attachments * Enable/disable audio stream from an opponent * Switch camera * Enable/disable camera * Mute/unmute the microphone * Display the number of chat members * Share a screen * Change media settings Choose the code sample below and head to GitHub repositories to explore and clone it. Go over the relevant documentation for more details. View on GitHub
Documentation
View on GitHub
Documentation
View on GitHub
Documentation
} > View on GitHub
Documentation
View on GitHub
Documentation
# Agents Source: https://docs.quickblox.com/docs/agents Learn how to manage your agents with QuickBlox. Agents are typically customer support representatives who handle conversations escalated by the AI Agent or the app user. Agents are a crucial part of any customer support or engagement system, especially in environments that rely on automated systems like the AI Agent. While AI Agents handle routine queries and provide quick responses, there are situations where more personalized, complex, or sensitive interactions require human intervention. This is where the agents functionality comes in. Agents play a vital role in enhancing the customer experience by ensuring that users receive the right level of support when automated responses are no longer sufficient. Whether it's resolving complex issues, providing expert knowledge, or handling user escalations, agents act as the bridge between automation and human expertise. The flexibility to escalate a conversation to an agent ensures that users are always supported, even in cases where an AI assistant can't provide the necessary assistance. Key benefits of the using agents include: * **Seamless User Experience**. Users either interact directly with an agent from the start or begin with the AI Agent and smoothly transition to an agent when human assistance is needed. * **Enhanced Support**. Agents are equipped with full context of the conversation history, enabling them to offer efficient and effective solutions. * **Scalability**. By having multiple agents available, organizations can scale their support team as needed, ensuring that no user query goes unanswered. * **Collaboration**. Agents can ensure smooth handoffs by assigning conversations to the most suitable team member. With these capabilities, the agents is a powerful tool for both automation-driven and human-powered support, delivering a more flexible and responsive system for users. Moreover, since agents are also QuickBlox users, they can interact with customers not only through the **Agent Desk**, but also directly within an app if integrated appropriately. ## Manage agents On the **Dashboard**, you can create, update, and delete agents. ### View list of agents Navigate to **Dashboard => YOUR\_APP => Agents**. Agents page screenshot The agents page also includes a direct **login link** for agents. This link can be shared with new agents so they can quickly log in. ### Create agent 1. Navigate to **Dashboard => YOUR\_APP => Agents**. 2. Click **Create** button at the top right corner. 3. Enter the agent’s **Full name**, **Login**, **Email**, and **Password** (password should be shared with the agent). 4. Click **Create** to finalize. Account owner can also create an agent profile for himself (by checking **"Create agent profile for me"** option). This allows account owner to act as an agent. Create agent screenshot ### Update agent 1. Navigate to **Dashboard => YOUR\_APP => Agents**. 2. On agents list, click on agent’s ID. 3. Edit the user profile (as each agent is a QuickBlox user, so edit user page opened). 4. Click **Save and close** button to save changes. Update user details screenshot ### Delete agent 1. Navigate to **Dashboard => YOUR\_APP => Agents**. 2. Check one or more agents you want to delete. 3. Click **Remove selected** at the bottom of the list. Confirm the removal. Delete agents screenshot When the account owner deletes an agent associated QuickBlox user will be also deleted. All data associated with the user are deleted from the system. Thus: * All files associated with the user are deleted. * All custom objects associated with the user are deleted. * All dialogs where the user is a dialog owner are deleted. If a user is a dialog occupant and not a dialog owner, their ID is deleted from the occupants IDs list of all group dialogs they participate in. However, a user isn’t removed from occupants IDs list of private dialogs. ## Agent Desk Agents have their own space named **Agent Desk**. This interface is designed to help agents track incoming conversations, handle user queries, and collaborate with teammates. ### Log in as agent Login URL located in the Description dropdown on the Agents page. 1. Navigate to **Dashboard => YOUR\_APP => Agents** 2. Copy login URL from the Description dropdown 3. Share URL with agents. Agents page screenshot Agents may login using this URL. Agent Desk login ### View message history Agents see a list of active and pending conversations in real time. When agent opens a conversation, next elements can be seen: * **Message History**. A scrollable history of previous user messages and AI Agent responses. * **Message Input**. Where agents type their replies. * **Assign Button**. Allows agent to transfer the current conversation to another agent. Agent Desk interface screenshot ### Change agent online status 1. In the **Agent Desk** left menu, click on agent name. 2. Click on Online or Offline option. This status determines whether new conversations will be routed to them. Based on the configuration in the AI Agent settings: * If '**Assign to online agents only**' is enabled, only agents marked as online will receive new conversations. * If '**Assign to online and offline agents**' is enabled, conversations may be routed to offline agents, but only if no online agents are available. Agent Desk change online status screenshot ### Update agent profile 1. In the **Agent Desk** left menu, click on agent name. 2. Click Profile. 3. Change information. 4. Save. Login and email may be updated only by account owner. Update agent profile # Application Source: https://docs.quickblox.com/docs/application Learn how to create and manage your app. Before starting to build your chat app for a particular platform, it's important to understand that your QuickBlox application has many functionalities to keep the chat service running along with basic features to provide proper application management. You can integrate only one QuickBlox application into your app, regardless of the platform. Thus, if you develop an app for Android, iOS, or web platform, you require only one QuickBlox application. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Create your first application After signing up, you can start creating your first QuickBlox application. ## Get application credentials Each QuickBlox application has application credentials required for initialization. To get application credentials, follow the steps below: ## Edit application You can edit your application details. To edit the application details, follow the steps below: ## Delete application You can delete an application. Once you delete the application, you can no longer access any of the data within that app. To delete the application, follow the steps below: ## View list of API keys You can view a list of available API keys in the app, to do this please follow steps below: Navigate to the **Dashboard => *YOUR\_APP* => General => API keys**. ## Create API key You can create up to 10 **API keys**. To create a new API key please follow steps below: ## Set default join required for new group dialogs This setting applies **only to group dialogs**. Be careful when changing this setting, as it directly affects the behavior of your application in production. Prior to **server version 2.34.0**, all group dialogs required participants to join before sending or receiving real-time messages. There was no configurable parameter for this behavior. Starting from **server version 2.34.0**, a new **join required** setting has been introduced in the application settings. This setting defines the default value of the `is_join_required` field for all **newly created** group dialogs within the application. * **New applications** created from version 2.34.0 onwards have **join required disabled** by default. * **Existing applications** created before version 2.34.0 retain the previous behavior with **join required enabled** by default. To migrate an older application to the new behavior where joining is not required, disable this setting. Changing this setting does **not** affect existing dialogs. It only applies to dialogs that are created **after** the change. To fully migrate to the new behavior, you need to: 1. Migrate existing dialogs to the new behavior by setting `is_join_required` to `false`. 2. Update your client application in accordance with the new behavior. To set default join required follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => General => Settings**. 2. Enable or disable the **join required** setting. ## Set session permissions You can configure session permissions to setup proper privacy level. By default, for development simplicity everything is allowed, but for production environment it is recommended to setup more restrictive configuration: * Implement user management on own backend and disable possibility to create users with application/user session. * Choose carefully what data considered as PII are allowed to be stored within User and retrieved by other users. To set session permissions follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => General => Settings**. 2. Check/uncheck chosen options. 3. Click the **Save** button. # Chat Source: https://docs.quickblox.com/docs/chat-dashboard Learn how to manage dialogs and messages. Chat is one of the core features of QuickBlox. Here, you will learn how to create and manage dialogs and messages via the Dashboard. You will also learn how to set up automatic push notifications for offline users. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## View list of dialogs To view a list of dialogs, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat** tab. 2. Use a **Search** area to view a dialog by dialog ID, dialog name, occupants IDs, last message, etc. ## Create dialog To create a dialog, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat** tab. 2. Click the **New Dialog** button. 3. Enter the name of the dialog into the **Name** field. 4. Enter a photo ID/UID or URL of the photo. 5. Specify if the dialog is a group or public group. 6. Specify IDs of opponents who are going to participate in the dialog (for group dialog only). 7. Click the **Create** button. Let's see what capabilities an account owner has with regard to the dialog type. | Capabilities | Private | Group | Public | | ------------- | ------- | ----- | ------ | | Create dialog | ✗ | ✓ | ✓ | ## Update dialog To update a dialog, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat** tab. 2. Click the ID of the dialog in the **ID** column. 3. Update dialog fields. 4. Click the **Save and close**. Let's see what capabilities an account owner has with regard to the dialog type. | Capabilities | Private | Group | Public | | ------------------------ | ------- | ----- | ------ | | Update a dialog name | ✗ | ✓ | ✓ | | Update a photo | ✗ | ✓ | ✓ | | Add occupants | ✗ | ✓ | ✗ | | Remove yourself | ✗ | ✓ | ✗ | | Remove occupants | ✗ | ✓ | ✗ | | Update custom parameters | ✗ | ✗ | ✗ | If an account owner removes a dialog occupant, the occupant ID is removed from the occupants IDs list of this dialog. As a result, the dialog is removed from the user's list of dialogs. However, the dialog remains in the history of other users who are still present in the occupants IDs list. ## Delete dialog To delete a dialog, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat** tab. 2. Find the dialog. You can use the search or just locate it in the list of dialogs. 3. Check a dialog you want to delete. 4. Click the **Remove selected**. Let's see what capabilities an account owner has with regard to the dialog type. | Capabilities | Private | Group | Public | | --------------------------- | ------- | ----- | ------ | | Delete dialog for all users | ✓ | ✓ | ✓ | ## View messages history To view messages history, follow the steps below: 1. Navigate to **Dashboard => *YOUR\_APP* => Chat** tab. 2. Find the dialog. You can use the search or just locate it in the list of dialogs. 3. Click the **View** button in the **History** column. ## Delete message To delete a message, follow the steps below: 1. Navigate to **Dashboard => *YOUR\_APP* => Chat** tab. 2. Find the dialog. You can use the search or just locate it in the list of dialogs. 3. Click the **View** button in the **History** column. 4. Check the messages you want to delete. 5. Click the **Remove selected** button. ## Enable offline messaging Send notifications about new messages to the user when their app is not running by using offline messaging functionality. This functionality allows to customize automatic push notifications displayed on a user's device when they are offline. In other words, if your opponent is offline when you are writing a message, they automatically receive a push notification. Make sure to subscribe your users to pushes using SDKs. Review [Push Notifications](/sdks/ios-push-notifications) section for more details. Only FCM data messages can be sent from the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages in your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notifications. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. Enable offline messaging from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Check the **Enable automatic push notifications for offline users** if you want to receive push notifications from the server automatically. ## Configure offline messaging template You can configure an offline messaging template from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Choose the template in the **Templates** section. Available for Growth/HIPAA Cloud/Enterprise plan You can set the custom template in the **Offline messaging text** section using the following template variables: * %unread\_count% - total number of new messages the recipient has * %plural\[new message]% - words in brackets (20 characters max) will be pluralized based on unread messages count * %body\[x]% - quote x characters from message body * %recipient\_fullname% - full name of recipient * %sender\_fullname% - full name of sender * %recipient\_login% - login of recipient * %sender\_login% - login of sender * %dialog\_name% - name of chat dialog (will be replaced with "Private dialog" string for 1-1 messages) 1. Set the **Badge counter** to include counter info into your push message. Useful to include an unread counter that is the number of unread messages a user has. 2. Set a push notification sound in the **Sound** section (for iOS only). 3. Set the **Category (iOS)** field. Enter "actions" that should be presented in the app notification in various views (for iOS only). 4. Enable the **Content Available** option to indicate that new content is available (for iOS only). 5. Enable the **Mutable content** option to modify the content of newly delivered notifications before the user is alerted. Use a notification service app extension to be able to modify the content of the notification. If the option is enabled, the system passes the notification to the service app extension for the subsequent modification of the notification payload before the actual delivery (for iOS only). 6. Click the **Save** button. Currently, push notifications are supported in a mobile environment only. ## Ban user You can ban a user account so the user can no longer connect to the chat. See the [ban user page](/docs/chat-moderation#ban-user) to learn how to ban a user. # Chat Moderation Source: https://docs.quickblox.com/docs/chat-moderation Learn how to moderate messages and users in your app. This feature is available for customers on the Enterprise plan only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). ## Trust & Safety (TnS) QuickBlox TnS helps you moderate chat messages in real-time. Whenever a user sends a message to the dialog, the functionality blocks potentially offensive messages from being delivered to the recipient or masks profanity or sensitive information such as social security numbers, addresses, names, and phone numbers, which helps prevent the loss of sensitive data. TnS settings are not mixed up between multiple apps in one account. You can configure different TnS settings for each app. ### Add rule A **rule** defines moderation criteria based on the words or regexp that should be filtered. To create a moderation rule follow the steps below: 1. Go to the **Dashboard => *YOUR\_APP* => Chat => TnS** section. Click the **Add message rule** button. addons-moderation-chat-tns-add_message_rule.png 1. Enter the name of the rule into the **Name** field. 2. Move to the **Mask** field and enter the symbols to be used instead of the inappropriate word. You can leave it empty if you don't want to use the mask. 3. Move to the **Score** field and enter a rule score indicating how bad a word is. Set a value from **0** to **1000** for this field. See how to calculate a message score [here](/docs/chat-moderation#how-message-score-is-calculated). 4. Use either a **RegExp field** or **Find dictionary** to save inappropriate words or expressions. * The **RegExp** option allows entering a regular expression, which is a sequence of characters that define a search pattern; used mainly for pattern matching with strings. * The **Find dictionary** option allows uploading a file with words that should be filtered. If you want to upload a dictionary file, use the **.txt** file. The cognates order in the dictionary should be from the longest to the shortest. See the screenshot below: tns-dictionary.png 5. Click the **Save rule** button. app-restyle-chat-tns-new-rule.png The last created rule has the highest priority either it was created with the dictionary or RegExp value. ### Add score group A **score group** defines blocking behavior based on the score provided and works in conjunction with the message score. See how to calculate a message score [here](/docs/chat-moderation#how-message-score-is-calculated). To add a score group, proceed as follows: 1. Go to the **Dashboard => *YOUR\_APP* => Chat => TnS** section. Click the **Add score group** button. addons-moderation-chat-tns-add_score_group.png 1. Enter the name of the score group into the **Name** field. 2. Set a score value into the **Score** field. Enter the value from **1** to **100 000** for this field. The message is blocked by the TnS system if the message score is **more than** or **equals** the group score. If the message score is **less than** the score group, the words or RegExp are masked by the TnS system. This is the case if the mask was set. 1. Click the **Save rule** button. app-restyle-chat-tns-new-score.png A **Block** option enables the score group. This means that blocking functionality is up and running. Thus, if a message is blocked, it isn't delivered to the recipient. The score group with the lowest score value has the highest priority. ### How message score is calculated A message score is a score assigned to a message based on the [rule score](/docs/chat-moderation#add-a-rule). If there are several inappropriate words in the message, a total score of all words is calculated. *Example 1:* An inappropriate word is repeated 3 times in a single message. According to the message rule, this word is given 2 scores. However, since it is repeated 3 times, the total message score equals 6. *Example 2:* There are 2 inappropriate words in a single message. However, these two words come from different rules. One word is assigned 3 scores and another word is assigned 2 scores according to different rules. Thus, the total message score equals 5. ### How it works Once you've set a message rule and score group, let's check how message blocking/masking functionality works: 1. Send a message with an inappropriate word which score is less than the value set in the score group. 2. Go to the **Chat** tab and click the **view** button in the **History** column for the required dialog. You can see the **Messages History** pop-up showing that the message is masked and is received by the recipient. addons-moderation-chat-tns-history-masked.png 1. Send a message with an inappropriate word which score is more than or equals the value set in the score group. 2. Go to the **Chat** tab and click the **view** in the **History** column for the required dialog. You can see the **Messages History** pop-up showing that the message is displayed with the **blocked\_by=\** custom parameter and is not delivered to the recipient. addons-moderation-chat-tns-history-blocked.png ## Ban user You can ban a user account so the user can no longer connect to the chat. If a banned user tries to connect to chat, the system returns an error saying: “You are banned. Please contact your app customer support.” 1. Go to the **Dashboard => *YOUR\_APP* => Chat => Bans** tab. Click the **Add ban** button. addons-moderation-chat-bans-list.png 1. Enter the user ID that is going to be banned into the **QB User ID** field. 2. Enter the ban expiration date into the **Expires** field. 3. Click the **New ban** button. addons-moderation-chat-bans-new_ban.png # Content Source: https://docs.quickblox.com/docs/content-dashboard Learn how to store and access files with QuickBlox file storage. On this page, you will learn how to manage QuickBlox file storage using the Dashboard. QuickBlox file storage allows storing media files such as photos, video, documents, etc. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## View list of files To view a list of files, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Content** page. Here, you will see a list of uploaded files. 2. Use a **Search** area to display a file/files by file ID, user ID, or file name. ## Upload file To upload a file to the file storage, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Content** page. Here, you will see a list of uploaded files. 2. Click the **Browse** button and choose the file to be uploaded. 3. Click the **Upload** button. The maximum size of the uploaded file depends on the membership plan. | Basic | Startup | Growth | HIPAA | Enterprise | | | --------------- | ------- | ------ | ----- | ---------- | --------------------------------------------------------------- | | File size limit | 10 Mb | 25 Mb | 50Mb | 50Mb | [Contact our sales team](https://quickblox.com/enterprise/#get) | ## Download file To download a file, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Content** page. Here, you will see a list of uploaded files. 2. Choose a file you want to download. 3. Click the **Download** button in the **Contents** column for this file. To download a graphic file: 1. Click the **View** button. 2. Right-click on the image. 3. Click the **Save image as** menu item. ## Delete file To delete a file, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Content** page. Here, you will see a list of uploaded files. 2. Check the file/files you want to delete. 3. Click the **Remove selected** button. # Custom Identity Provider Source: https://docs.quickblox.com/docs/custom-identity-provider Learn how to easily integrate your existing user base into QuickBlox. This feature is available for customers on the **Enterprise plan** only. QuickBlox Custom Identity Provider (CIdP) allows you to use **external authentication**. In this case, user credentials and password management are handled by Identity Provider (IdP). When CIdP is enabled and configured, users can log in to QuickBlox API with token generated by a service external to QuickBlox. This is especially helpful when you have existing user base. ## Recommended workflow **Application** - web or mobile application that integrates QuickBlox. **Identity provider (IdP)** - service that authenticates and verifies user credentials, it's external to QuickBlox. **IdP\_user\_ID** - user ID in external service. **IdP\_token** - access token to external service. 1696 **Detailed description:** 1. The application logs in user to the identity provider. 2. The identity provider returns the **IdP\_user\_ID** and **IdP\_token**. 3. The application logs in user to QuickBlox. **IdP\_user\_ID** and **IdP\_token** must be set as `user[login]` and `user[password]` (see [Create Session](https://docs.quickblox.com/reference/create-session)). 4. The QuickBlox server sends **IdP\_user\_ID** and **IdP\_token** to identity provider for verification. 5. The identity provider checks that credentials are correct and sends user details back to QuickBlox server. 6. The QuickBlox server parses response from IdP and creates a new QuickBlox user if it doesn't exist. 7. The QuickBlox server sends a **user** session token back to the application. ## Step 1: Create verification endpoint Identity provider must provide interface for QuickBlox to verify users - verification endpoint. This endpoint will be triggered every time when user tries to log in to QuickBlox API. There are no strict rules how endpoint must look. The only requirement it must receive some **credentials**, for example, token, and return **unique identifier** of the user. You can send additional information about user that can be parsed into QuickBlox user, for example, full name or email, the full list is available [here](#response-params). **Example:** In this example, `IdP_user_ID` and `IdP_token` are credentials, `user.id` is unique identifier. ```HTTP HTTP theme={null} GET https://youripserver.com/users/identity?IdP_user_ID=4324&IdP_token=8b75a6c7191285499d890a81df4ee7fe49bc732a ``` ```JSON JSON theme={null} { “user”: { “id”: 4324, “login”: “samuel456”, “full_name”: “Samuel Johnson” } } ``` **Note** If identity provider returns an error QuickBlox will resend the error **as is** to the application. ## Step 2: Configure CIdP Once verification endpoint is set and running, you can configure QuickBlox CIdP. 1. Go to the **Dashboard => *YOUR\_APP* => Overview** page. 2. Locate the **Custom Identity Provider** groupbox at the bottom of the page. 3. Check **Enable custom Identity Provider authentication**. 4. Fill in the [API URL](#api-url) field with your verification request URL. 5. Choose a request type from the dropdown: GET or POST. 6. Fill in [Request headers](#request-headers) or [Request params](#request-params). 7. Fill in [Response params](#response-params). 8. Click the **Update** button. Now users authenticate via your Identity Provider. 1600 ### API URL URL of the verification request. It will be triggered every time when user tries to log in to QuickBlox API. **Example:** [https://youripserver.com/users/identity](https://youripserver.com/users/identity) ### Request headers Headers of the verification request. Header name can be any, as a value you can set one of available variables. Here you define how to map params of QuickBlox [Create Session](https://docs.quickblox.com/reference/create-session) request to headers of your verification request. You can pass data via request headers or request params, both are valid and optional. **Format:** `{"Header-1": "#{variable-1}", "Header-2": "#{variable-2}"}` *\*All underscore(\_) characters will be replaced with hyphen(-) characters here.* **Example:** `{"IdP_user_ID": "#{login}", "IdP_token": "#{password}"}` **Allowed variables:** | Variable | Type | Description | | -------- | ------ | ----------------------------------------------------------------------------------------------------- | | login | string | Value of user\[login] (see [Create Session](https://docs.quickblox.com/reference/create-session)). | | password | string | Value of user\[password] (see [Create Session](https://docs.quickblox.com/reference/create-session)). | | email | string | Value of user\[email] (see [Create Session](https://docs.quickblox.com/reference/create-session)). | ### Request params Params of the verification request. Param name can be any, as a value you can set one of available variables. Here you define how to map params of QuickBlox [Create Session](https://docs.quickblox.com/reference/create-session) request to params of your verification request. You can pass data via request headers or request params, both are valid and optional. **Format:** `{"param_1": "#{variable-1}", "param_2": "#{variable-2}"}` **Example:** `{"IdP_user_ID": "#{login}", "IdP_token": "#{password}"}` **Allowed variables:** | Variable | Type | Description | | -------- | ------ | ----------------------------------------------------------------------------------------------------- | | login | string | Value of user\[login] (see [Create Session](https://docs.quickblox.com/reference/create-session)). | | password | string | Value of user\[password] (see [Create Session](https://docs.quickblox.com/reference/create-session)). | | email | string | Value of user\[email] (see [Create Session](https://docs.quickblox.com/reference/create-session)). | **Note** `GET` request will use query params. `POST` request will use body params. ### Response params Response parameters are received as a result of the verification request. Here you define how to map received data to QuickBlox user. QuickBlox will create **user** session if response is valid and parsed. New user will be created if **UID** is not found in QuickBlox database. **Format:** `{"qb_user_field_1": "#{param_1}", "qb_user_field_2": "#{param_2}", "qb_user_field_3": "#{param_3}"}` **Example:** `{"uid": "#{user.id}", "login": "#{user.login}", "full_name": "#{user.full_name}", "external_user_id": "#{user.id}"}` ```JSON JSON theme={null} { “user”: { “id”: 4324, “login”: “samuel456”, “full_name”: “Samuel Johnson” } } ``` **Allowed QuickBlox user fields:** | Field | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------- | | `uid` | string | yes | Unique identifier. Hidden system field that links QuickBlox user with external system. | | `login` | string | no | User's login (see [Users](https://docs.quickblox.com/reference/user-model)). Generated based on **UID** if not specified. | | `email` | string | no | User's email (see [Users](https://docs.quickblox.com/reference/user-model)). Empty if not specified. | | `full_name` | string | no | User's full name (see [Users](https://docs.quickblox.com/reference/user-model)). Empty if not specified. | | `external_user_id` | int | no | ID of user in external system (see [Users](https://docs.quickblox.com/reference/user-model)). Empty if not specified. | ### Allow reuse QuickBlox user Any user registered with CIdP has **UID**. This system field links QuickBlox users and external users. If **UID** doesn’t exist in the QuickBlox database - a new user will be created. This checkbox tells how to act when external service responds with **UID** that is already in use and QuickBlox user exists: * If checked - QuickBlox session is created with an existing user. * If not checked - QuickBlox session is created with a new user, **UID** will be assigned to the new QuickBlox user. It's recommend to set **UID** as user\_id in your system. ## Step 3: Implement app authentication On the application side you must implement regular login/password authentication. It's important to set your credentials into `login` and `password` parameters, for example, put **IdP\_user\_ID** to `login` and **IdP\_token** to `password`. This is done for easy migration of applications that are already published on Apple Store and Google Play. ```bash theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"gqneCwWtz8dqF4F", "timestamp":"1567350955", "nonce":"71334", "signature":"1924a2c84b96347d66b6b2c8489a3cf1d35a8d74", "user":{ "login":"4324", "password":"8b75a6c7191285499d890a81df4ee7fe49bc732a" } }' \ https://api.quickblox.com/session.json ``` ```javascript theme={null} let externalUserId = "4324"; let externalUserToken = "8b75a6c7191285499d890a81df4ee7fe49bc732a"; QB.createSession({login: externalUserId, password: externalUserToken}, function(err, result) { }); ``` ```swift theme={null} let externalUserId = "4324" // let externalUserToken = "8b75a6c7191285499d890a81df4ee7fe49bc732a" // QBRequest.logIn(withUserLogin: externalUserId, password: externalUserToken, successBlock: { (response, user) in }, errorBlock: { (response) in }) ``` ```kotlin theme={null} val externalUserId = "4324" val externalUserToken = "8b75a6c7191285499d890a81df4ee7fe49bc732a" val user = QBUser() user.login = externalUserId user.password = externalUserToken QBUsers.signIn(user).performAsync(object : QBEntityCallback { override fun onSuccess(qbUser: QBUser?, bundle: Bundle?) { } override fun onError(e: QBResponseException?) { } }) ``` If you can't find code example for your language or platform, check Authentication page of the SDK that you choose. ## Step 4: Implement Chat connection When CIdP is enabled you must connect to chat with QuickBlox user ID and session token. ```javascript theme={null} var session = QB.service.getSession(); var params = {userId: session.user_id, password: session.token}; QB.chat.connect(params, function(error, contactList) {}); ``` ```swift theme={null} let userID = QBSession.current.currentUserID let userPassword = QBSession.current.sessionDetails?.token QBChat.instance.connect(withUserID: userID, password: userPassword, completion: { (error) in }) ``` ```kotlin theme={null} val user = QBUser() user.id = 47892 user.password = QBSessionManager.getInstance().token QBChatService.getInstance().login(user, callback) ``` # Custom Objects Source: https://docs.quickblox.com/docs/custom-objects-dashboard Learn how to store and sync data with QuickBlox key-value storage. Custom Objects module provides flexibility to define any data structure (schema) you need, build one-to-many relations between schemas and control permissions for all operations made on data. The schema is defined in QuickBlox Dashboard. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## View list of classes To view a list of classes, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Click the **Current Class** dropdown to view a list of classes. ## Create class To create a custom object class, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Click the **Add** dropdown and select **Add new class** menu item. 1. Enter the name of the class. 2. Enter the name of the field. 3. Choose the type of the field. 4. Check if the new field is of the array type. 5. Add as many fields as you need by clicking the **Add field** button. 6. Click the **Create class** button. ## Delete class To delete a class, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click the **Remove** dropdown and select **Remove class** menu item. ## Add field To add a field to a class, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click the **Add** dropdown and select **Add field** menu item. 1. Enter the name of the field. 2. Choose the type of the field. 3. Check if the new field is of the Array type. 4. Click the **Add field** button. You can add as many fields to the class as you need. ## Remove field To remove a field from a class, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click the **Remove** dropdown and select **Remove field** menu item. ## View list of records To view a list of records, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Click the **Current Class** dropdown to choose a specific class. As a result, all records of this class are displayed on the page. ## Add record To add a custom object record, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click the **Add** dropdown and select **Add record** menu item. 1. Set fields of the record. The record fields are defined in the class. If the field value isn't specified, a null value is inserted automatically. 2. Click the **Add record** button. ## Add child record You can create a child record. A child record is a record that refers to another record that has already been created, a parent. When a record is created, it is assigned a record ID. Thus, you can create a new record, a child, and attach it to the parent record by pointing to its ID. See [this section](/docs/key-concepts#relations) to learn more about relations between records. To create a child record, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click the **Add** dropdown and select **Add record** menu item. 1. Set fields of the record. The record fields are defined in the class. If the field value isn't specified, a null value is inserted automatically. 2. Set a parent ID to refer to a parent record. 3. Click the **Add record** button. ## Edit record To edit a record, do the following: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click the ID of the record to edit. 1. Edit record fields. 2. Click the **Update record** button to save changes. ## Remove record To remove a record, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Check the record you want to delete. 3. Click the **Remove selected** button. remove record.png ## Set permissions You can set a permissions schema. There are two access levels in the permissions schema: class and record. Only one permission level can be applicable to the record: class permission schema or record permission schema. ### Class permissions Only the account owner can create a class in the Custom Objects module and make all possible actions with it. All actions (Create, Read, Update, and Delete) are available for the class entity and are applicable for all records in the class. Every action has a separate permission level available. The exception is a Create action that is not available for the **Owner** permission level. See [this section](/docs/key-concepts#permission-levels) to learn about permission levels. Default class permission schema is used while creating a class: * **Create**: Open * **Read**: Open * **Update**: Owner * **Delete**: Owner To set a permissions schema for the class, do the following: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click **Edit permission** button. 1. Set a permission level for each CRUD operation here. See [this section](/docs/key-concepts#permission-schema) to learn more about permission levels. 2. Check the boxes in **Use Class permissions** if you want to apply class permissions to all its records. 3. Click the **Edit permissions** button. A class permission schema has a higher priority than a record permission schema. If an **account owner** ticks the **Use Class permissions** checkbox, the record permissions are ignored. In case, an **account owner** doesn't tick the **Use Class permissions** checkbox, a user has a possibility to change permission levels for every separate record in the table or create a new one with the ACL that a user requires. ### Record permissions A record is an entity within the class in the Custom Objects module that has its own permission levels. All permission levels except for the **Not Allowed** are available for the record and there are only three actions available and applicable for the record: Read, Update, and Delete. See [this section](/docs/key-concepts#permissions) to learn more about permissions. Default Record permission schema is used while creating a class: * **Read**: Open * **Update**: Owner * **Delete**: Owner To set a permissions schema for the record, do the following: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click the ID of the record to edit. 4. Click the **Set permissions on record** button. edit_record_permissions.png 1. Set a permission level for each CRUD operation here. See [this section](/docs/key-concepts#permission-levels) to learn more about permission levels. 2. Click the **Update record** button to save changes. A class permission schema has a higher priority than a record permission schema. If an **account owner** ticks the **Use Class permissions** checkbox, the record permissions are ignored. See [this section](/docs/custom-objects-dashboard#class-permissions) for more details. ## Import data You can import data in CSV or JSON format. To import data, do the following: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click **Browse** to choose a CSV or JSON file in a local file system. 4. Click **Import Data**. import class records.png ## Import schema You can import a class schema. The class schema defines class fields and their data types in .yml format. To import a class schema, do the following: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom** page. 2. Select a class in the **Current Class** dropdown. 3. Click the **Browse** to choose a .yml file in a local file system. 4. Click **Import Schema**. import schema.png # Data Export Source: https://docs.quickblox.com/docs/data-export Learn how to export data from the Dashboard. This feature is available for customers on the Enterprise plan only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). Users should be able to retrieve and reuse their personal data for their own purposes in different services. Therefore, we provide a possibility to export data in a format that can be read and imported into any program. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Export users To export users data, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Users** page. Here you can see a list of users. 2. Check users you want to export or leave them unchecked if you want export data of all users. 3. Click the **Export selected** button. You also can export all users by clicking the **Export all** button. As a result, the data are exported in CSV format. app-users.png ## Export dialogs history To export the history of dialogs, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat** page. Here you can see a list of dialogs. 2. Check the dialogs if you want to export chat history for particular dialogs or leave them unchecked if you want to export chat history for all dialogs. 3. Click the **Export history** button. As a result, the data are exported in CSV format. app-chat.png ## Export custom objects To export custom objects, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Custom => Export** page. 2. Choose a class in the **Class** dropdown to list records of a particular class. 3. Export custom objects in either of three ways: * **Export to csv** - allows exporting class records in CSV format. * **Export to json** - allows exporting class records in JSON format. - **Export schema** - allows exporting class schema in YML format. app-custom-export.png ## Export profile data To export the profile data, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Account settings => Privacy and Settings => Actions** section. 2) Click the **Export profile csv** to export data in CSV format. app-privacy and settings.png # Glossary Source: https://docs.quickblox.com/docs/glossary ## **A** [Application ID](#application-id) A unique identifier for an application generated automatically upon application creation. You can have more than one application in your account. Each application will have a unique application ID in this case. [Authentication provider](#authentication-provider) A specific authentication mechanism integrated into the QuickBlox user authentication system. QuickBlox provides several authentication providers such as login/email and password authentication, social authentication, and custom identity provider authentication that you can use in your client application to allow users to log in. [Authentication](#authentication) The process of proving you are who you say you are. In other words, to access QuickBlox APIs you need to authenticate your application with the QuickBlox server. When the authenticity is verified, the server responds to the request. [Authorization](#authorization) Granting an authenticated party the permission to do something using server API. It specifies what data the user is allowed to access and what they can do with that data. ## B [Blob](#blob) A specific entity for a file in the QuickBlox system. Consists of file metadata stored in QuickBlox database and binary data stored as a single entity in the cloud storage (S3 bucket in case of AWS deployment). Blobs are typically images, audio, or other multimedia objects. Learn more about blob [here](https://docs.quickblox.com/reference/content). [BOSH protocol](#bosh-protocol) A bidirectional HTTP-based transport protocol that creates a long-lived TCP connection between the client and server. ## C [CGI escape](#cgi-escape) The function is used for query parameters only. It comes from the CGI specification describing how data should be encoded/decoded between the web server and application; used for escaping a URL value in the query string. All characters that don't fall into the ALPHA, DIGIT, '\_', '-', '.' and ' ' character set get escaped. ## D [Dashboard](#dashboard) A user-friendly interface that enables you to create new apps and manage your existing apps from here. It provides an at-a-glance view of QuickBlox modules. Get your application credentials (Application ID, Authorization Key, Authorization Secret, and Account Key) from here to launch the framework. [Dialog](#dialog) Represents an entity for a chat within the QuickBlox system. Each dialog has a unique dialog ID generated automatically by the server after dialog creation. There are 3 dialog types: private, group, and public. Learn more about QuickBlox dialog types [here](/docs/key-concepts#chat). ## E [Event](#event) An entity for a push notification event within the QuickBlox system. Each event has a unique event ID generated automatically by the server after event creation. [External user ID](#external-user-id) A unique identifier of the user in the integrated external system, used for cross-referencing. For example, a user has an internal user ID within the QuickBlox system and external ID, provided by the integrated external (client) system. When an API call with reference to the user has to be submitted, the user can be referenced by either internal or external identifiers. ## H [HIPAA](#hipaa) Health Insurance Portability and Accountability Act (HIPAA) sets the standard for the confidentiality of protected health information (PHI). [HIPAA compliant chat](#hipaa-compliant-chat) A secure chat solution for healthcare. It enables telemedicine apps to deliver open, comfortable communication to patients and protect their sensitive information by implementing all the necessary security measures. [HMAC-SHA](#hmac-sha) In cryptography, an HMAC (sometimes expanded as either keyed-hash message authentication code or hash-based message authentication code) is a specific type of message authentication code (MAC) involving a cryptographic hash function and secret cryptographic key. It may be used to simultaneously verify both the data integrity and authentication of a message, as with any MAC. Any cryptographic hash function, such as SHA-256 or SHA-3, may be used in the calculation of an HMAC; the resulting MAC algorithm is termed HMAC-X, where X is the hash function used (for example, HMAC-SHA256 or HMAC-SHA3). The cryptographic strength of the HMAC depends upon the cryptographic strength of the underlying hash function, the size of its hash output, and the size, and quality of the key. ## I [Identity provider](#identity-provider) A service that stores and verifies user identity. Identity Providers (IdP) are typically cloud-hosted services, and they often work with single sign-on (SSO) providers to authenticate users. ## K [Key-value storage](#key-value-storage) A simple database that uses a simple key/value method for data storage. You can address your values by special identifier - key. The key is a string that will identify your value in storage. Value can be a boolean, number, or string type. ## M [Mesh](#mesh) A multi-point video routing topology where every participant sends its media stream to each participant and receives it from them. [Message](#message) Represents a message entity within the QuickBlox system. Each message has a unique message ID generated automatically by the server after message creation. Messages can contain any data - arrays, objects, numbers, and strings. [MIME content type](#mime-content-type) Multipurpose Internet Mail Extensions (MIME) is an Internet standard extending the format of email messages by supporting text in character sets other than ASCII, and attachments such as audio, video, images, and application programs. ## P [Peer-to-peer](#peer-to-peer) A computer networking architecture in which all participating peers have equal privileges and share the workload. Peer-to-peer differs from a client-server network architecture, where multiple client nodes connect to centralized servers for services. ## Q [QuickBlox API](#quickblox-api) An application programming interface (API) enabling data transmission between the application and QuickBlox system. It consists of different resources representing data associated with chat, push notifications, users, custom objects, etc. activities. It allows the application to interact with the QuickBlox system over standard HTTP protocols. Learn more about QuickBlox API basics form [here](https://docs.quickblox.com/reference/overview). [QuickBlox SDK](#quickblox-sdk) A software development kit (SDK) is a set of software development tools that allow the creation of applications for a certain software package, software framework. In other words, it is the interface built on top of the QuickBlox API. It allows the application to interact with the QuickBlox system. Designed for platforms such as iOS, Android, Web, React Native, and Flutter. ## S [Session token](#session-token) A token is a unique string identifying a user or application and providing temporary access to QuickBlox APIs. The token is valid only within the session it was generated. Learn more about the session token from [here](https://docs.quickblox.com/reference/authentication#application-vs-user-session-token). [SFU](#sfu) Selective Forwarding Unit (SFU) is a video routing topology allowing WebRTC clients to send their encoded video stream to the centralized media server where it is then forwarded/routed to the other WebRTC clients. The SFU allows for receiving multiple media streams and then decide which of these media streams should be forwarded to which WebRTC clients. ## T [TCP](#tcp) Transmission Control Protocol (TCP) is used to establish a long-lived, stream-oriented connection between two entities, running on top of Internet Protocol (IP). Sometimes the application fails to maintain a long-lived TCP connection between entities due to the nature of the device or network. If this is the case, it is desirable to use an alternative connection method that emulates the behavior of a long-lived TCP connection, for example, Websocket or Bosh. [TCP socket](#tcp-socket) A stream-oriented socket enabling reliable IP communication using the Transmission Control Protocol (TCP). ## U [User](#user) Represents an entity for a user within the QuickBlox system. Each user has a unique user ID generated automatically by the server after dialog creation. ## W [WebSocket](#websocket) A transport protocol defined by a long-lived, bidirectional connection between the server and client that takes place over a TCP connection. ## X [XMPP protocol](#xmpp) An Extensible Messaging and Presence Protocol, a set of open technologies for instant messaging, presence, multi-party chat, voice and video calls, collaboration, lightweight middleware, content syndication, and generalized routing of XML data. Learn more about XMPP from [here](https://xmpp.org/about/technology-overview.html). # Introduction Source: https://docs.quickblox.com/docs/introduction Learn about QuickBlox platform features and documentation guidelines. QuickBlox provides you with ready-to-use chat and video calling solutions for your apps. Using documentation you can quickly integrate QuickBlox across multiple platforms and devices. ## Before you begin Register a QuickBlox account by visiting the [signup page](https://admin.quickblox.com/signup). It only takes a few minutes, and you can use this account to build your apps. Click the **Add new app** button and fill in the required information about your company to create a new app. Go to the Overview section on your Dashboard, and copy the **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** from there. ## Platform features Use QuickBlox to add rich chat and video calling features to your apps. Our platform has a modular structure. Here is a list of all QuickBlox modules and their features: ### Users | Feature | Platforms | Description | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | User management | [iOS](/sdks/ios-authentication#loginemail-and-password) \| [Android](/sdks/android-users) \| [JavaScript](/sdks/js-users) \| [React Native](/sdks/react-native-users) \| [Flutter](/sdks/flutter-users) \| [Server API](https://docs.quickblox.com/reference/users) | Create, update, delete, and list users. | | Standard login | [iOS](/sdks/ios-authentication#loginemail-and-password) \| [Android](/sdks/android-authentication#loginemail-and-password) \| [JavaScript](/sdks/js-authentication#loginemail-and-password) \| [React Native](/sdks/react-native-authentication#log-in-user) \| [Flutter](/sdks/flutter-authentication#log-in-user) \| [Server API](https://docs.quickblox.com/reference/create-session) | Log in with email/username and password. | | Social login | [iOS](/sdks/ios-authentication#social) \| [Android](/sdks/android-authentication#social) \| [JavaScript](/sdks/js-authentication#social) \| [Server API](https://docs.quickblox.com/reference/create-session) | Log in using social network accounts. | | Phone number login | [iOS](/sdks/ios-authentication#phone-number) \| [Android](/sdks/android-authentication#phone-number) \| [JavaScript](/sdks/js-authentication#phone-number) \| [Server API](https://docs.quickblox.com/reference/create-session) | Log in using a phone number. | | Custom identity provider login | [iOS](/sdks/ios-authentication#custom-identity-provider) \| [Android](/sdks/android-authentication#custom-identity-provider) \| [JavaScript](/sdks/js-authentication#custom-identity-provider) \| [Server API](https://docs.quickblox.com/reference/create-session) | Log in with a token generated by the identity provider. | | User tags | [iOS](/sdks/ios-users#update-user) \| [Android](/sdks/android-users#update-user) \| [JavaScript](/sdks/js-users#update-user) \| [React Native](/sdks/react-native-users#update-user) \| [Flutter](/sdks/flutter-users#update-user) \| [Server API](https://docs.quickblox.com/reference/users) | Assign tags to users and then address them separately by tags. | | Existing user database integration | [Custom identity provider](/docs/custom-identity-provider) | Easily integrate your existing user database with QuickBlox. | ### Chat | Feature | Platforms | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Private chat | [iOS](/sdks/ios-chat-dialogs#dialog-types) \| [Android](/sdks/android-chat-dialogs#dialog-types) \| [JavaScript](/sdks/js-chat-dialogs#dialog-types) \| [React Native](/sdks/react-native-chat-dialogs#dialog-types) \| [Flutter](/sdks/flutter-chat-dialogs#dialog-types) \| [Server API](https://docs.quickblox.com/reference/create-dialog) | Create a one-to-one chat. | | Group chat | [iOS](/sdks/ios-chat-dialogs#dialog-types) \| [Android](/sdks/android-chat-dialogs#dialog-types) \| [JavaScript](/sdks/js-chat-dialogs#dialog-types) \| [React Native](/sdks/react-native-chat-dialogs#dialog-types) \| [Flutter](/sdks/flutter-chat-dialogs#dialog-types) \| [Server API](https://docs.quickblox.com/reference/create-dialog) | Create a group chat between multiple users. | | Public chat | [iOS](/sdks/ios-chat-dialogs#dialog-types) \| [Android](/sdks/android-chat-dialogs#dialog-types) \| [JavaScript](/sdks/js-chat-dialogs#dialog-types) \| [React Native](/sdks/react-native-chat-dialogs#dialog-types) \| [Flutter](/sdks/flutter-chat-dialogs#dialog-types) \| [Server API](https://docs.quickblox.com/reference/create-dialog) | Create a public chat between hundreds of users. Anyone can freely join the public chat. | | Message history | [iOS](/sdks/ios-chat-messaging#retrieve-chat-history) \| [Android](/sdks/android-chat-messaging#retrieve-chat-history) \| [JavaScript](/sdks/js-chat-messaging#retrieve-chat-history) \| [React Native](/sdks/react-native-chat-messaging#retrieve-chat-history) \| [Flutter](/sdks/flutter-chat-messaging#retrieve-message-history) \| [Server API](https://docs.quickblox.com/reference/list-messages) | All users' messages are stored on the server. You can fetch them for a particular dialog. | | Presence | [iOS](/sdks/ios-chat-user-presence) \| [Android](/sdks/android-chat-user-presence) \| [JavaScript](/sdks/js-chat-user-presence) \| [React Native](/sdks/react-native-chat-user-presence) \| [Flutter](/sdks/flutter-chat-user-presence) | Subscribe to user presence updates and check user online status. | | Read receipts | [iOS](/sdks/ios-chat-messaging#mark-message-as-read) \| [Android](/sdks/android-chat-messaging#mark-message-as-read) \| [JavaScript](/sdks/js-chat-messaging#mark-message-as-read) \| [React Native](/sdks/react-native-chat-messaging#mark-message-as-read) \| [Flutter](/sdks/flutter-chat-messaging#mark-message-as-read) | Mark messages as read. Thus, users will know if their messages have been read by other users in the dialog. | | Delivery receipts | [iOS](/sdks/ios-chat-messaging#mark-message-as-delivered) \| [Android](/sdks/android-chat-messaging#mark-message-as-delivered) \| [JavaScript](/sdks/js-chat-messaging#mark-message-as-delivered) \| [React Native](/sdks/react-native-chat-messaging#mark-message-as-delivered) \| [Flutter](/sdks/flutter-chat-messaging#mark-message-as-delivered) | Mark messages as delivered. Thus, users will know if their messages have been successfully delivered to other users in the dialog. | | Typing indicators | [iOS](/sdks/ios-chat-messaging#send-typing-indicators) \| [Android](/sdks/android-chat-messaging#send-typing-indicators) \| [JavaScript](/sdks/js-chat-messaging#send-typing-indicators) \| [React Native](/sdks/react-native-chat-messaging#send-typing-indicators) \| [Flutter](/sdks/flutter-chat-messaging#send-typing-indicators) | Subscribe to typing events to indicate if the other user is typing a message at the moment. | | Attachments | [iOS](/sdks/ios-chat-messaging#send-message-with-attachment) \| [Android](/sdks/android-chat-messaging#send-message-with-attachment) \| [JavaScript](/sdks/js-chat-messaging#send-message-with-attachment) \| [React Native](/sdks/react-native-chat-messaging#send-message-with-attachment) \| [Flutter](/sdks/flutter-chat-messaging#send-message-with-attachment) \| [Server API](https://docs.quickblox.com/reference/create-message) | Send messages with attachment so users can share files during a chat, such as images, audio files, videos, and other file types. | | Extended messaging | [iOS](/sdks/ios-chat-messaging#send-message-with-extra-data) \| [Android](/sdks/android-chat-messaging#send-message-with-extra-data) \| [JavaScript](/sdks/js-chat-messaging#send-message-with-extra-data) \| [React Native](/sdks/react-native-chat-messaging#send-message-with-extra-data) \| [Flutter](/sdks/flutter-chat-messaging#send-message-with-extra-data) \| [Server API](https://docs.quickblox.com/reference/create-message) | Extend a message with additional fields. | | Unread message count | [iOS](/sdks/ios-chat-dialogs#get-number-of-unread-messages) \| [Android](/sdks/android-chat-dialogs#get-number-of-unread-messages) \| [JavaScript](/sdks/js-chat-dialogs#get-number-of-unread-messages) \| [React Native](/sdks/react-native-chat-dialogs#get-number-of-unread-messages) \| [Flutter](/sdks/flutter-chat-dialogs#get-number-of-unread-messages) | Display a number of unread messages in a particular dialog. | | Offline messages | [iOS](/sdks/ios-chat-offline-messaging) \| [Android](/sdks/android-chat-offline-messaging) \| [JavaScript](/sdks/js-chat-offline-messaging) \| [React Native](/sdks/react-native-chat-offline-messaging) \| [Flutter](/sdks/flutter-chat-offline-messaging) | Subscribe to push notifications about new messages when the app is not running. | | Contact list | [iOS](/sdks/ios-chat-offline-messaging) \| [Android](/sdks/android-chat-offline-messaging) \| [JavaScript](/sdks/js-chat-offline-messaging) \| [React Native](/sdks/react-native-chat-offline-messaging) \| [Flutter](/sdks/flutter-chat-offline-messaging) | Add and remove users from your contact list. | | User-to-user block | [iOS](/sdks/ios-chat-privacy-list) \| [Android](/sdks/android-chat-privacy-list) \| [JavaScript](/sdks/js-chat-privacy-list) | Add users to your privacy list to block incoming messages from them. | | Trust and safety | [Trust and safety](/docs/chat-moderation#trust--safety-tns) | Create rules to mask profanity or block messages that contain it. | | User ban | [User ban](/docs/chat-moderation#ban-a-user) | Ban a user so the user can no longer connect to the chat server. | | Chatbot | [Simple bot](https://quickblox.github.io/quickblox-javascript-sdk/samples/chat_bots/simple_bot/) \| [RiveScript bot](https://quickblox.github.io/quickblox-javascript-sdk/samples/chat_bots/rivescript/) | Integrate your chatbot with QuickBlox. | ### Video calling | Feature | Platforms | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | | Voice and video | [iOS](/sdks/ios-video-calling) \| [Android](/sdks/android-video-calling) \| [JavaScript](/sdks/js-video-calling) \| [React Native](/sdks/react-native-video-calling) \| [Flutter](/sdks/flutter-video-calling) | Create video sessions that can be video and voice, voice-only or video-only. | | One-to-one calls | [iOS](/sdks/ios-video-calling) \| [Android](/sdks/android-video-calling) \| [JavaScript](/sdks/js-video-calling) \| [React Native](/sdks/react-native-video-calling) \| [Flutter](/sdks/flutter-video-calling) | Create video calls between two users. | | Group calls | [iOS](/sdks/ios-video-calling) \| [Android](/sdks/android-video-calling) \| [JavaScript](/sdks/js-video-calling) \| [React Native](/sdks/react-native-video-calling) \| [Flutter](/sdks/flutter-video-calling) | Create video calls with up to 4 users at a time. | | Screen-sharing | [iOS](/sdks/ios-video-calling-advanced#screen-sharing) \| [Android](/sdks/android-video-calling-advanced#screen-sharing) \| [JavaScript](/sdks/js-video-calling-advanced#screen-sharing) | Share a screen across video session participants. | | Mute/unmute | [iOS](/sdks/ios-video-calling-advanced#mute-audio) \| [Android](/sdks/android-video-calling-advanced#mute-audio) \| [JavaScript](/sdks/js-video-calling-advanced#mute-audio) \| [React Native](/sdks/react-native-video-calling-advanced#mute-audio) \| [Flutter](/sdks/flutter-video-calling-advanced#mute-audio) | Disable own or opponent’s audio/video stream. | | Camera switch | [iOS](/sdks/ios-video-calling-advanced#switch-camera) \| [Android](/sdks/android-video-calling-advanced#switch-camera) \| [JavaScript](/sdks/js-video-calling-advanced#switch-camera) \| [React Native](/sdks/react-native-video-calling-advanced#switch-camera) | Switch between camera input during a call. | | Video mirroring | Supported | Flip the video horizontally during a call. | | Offline calls | Supported | Implement offline calls workflow with QuickBlox push notifications. | ### Video conference | Feature | Platforms | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | Voice and video | [iOS](/sdks/ios-video-conference) \| [Android](/sdks/android-video-conference) \| [JavaScript](/sdks/js-video-conference) \| [Flutter](/sdks/flutter-video-conference) | Create video conference sessions that can be video and voice, voice-only or video-only. | | Group conference | [iOS](/sdks/ios-video-conference#conference-session) \| [Android](/sdks/android-video-conference#create-session) \| [JavaScript](/sdks/js-video-conference#create-session) \| [Flutter](/sdks/flutter-video-conference#create-session) | Create conference calls between 10-12 users at a time. | | Join/rejoin | [iOS](/sdks/ios-video-conference#join-video-room) \| [Android](/sdks/android-video-conference#join-video-room) \| [JavaScript](/sdks/js-video-conference#join-video-room) \| [Flutter](/sdks/flutter-video-conference#join-video-room) | Join a meeting or continue an earlier meeting with those who were in it. | | Mute/unmute | [iOS](/sdks/ios-video-conference#mute-local-audio) \| [Android](/sdks/android-video-conference#mute-local-audio) \| [JavaScript](/sdks/js-video-conference#mute-remote-audio) \| [Flutter](/sdks/flutter-video-conference#mute-local-audio) | Disable own or opponent’s audio/video stream. | | Camera switch | Supported | Switch video inputs during a call. | | Call recording | Supported | Enable server-side recording which means any conference call can be documented. | | Offline calls | Supported | Implement offline calls workflow with QuickBlox push notifications. | ### Content | Feature | Platforms | Description | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | File storage | [iOS](/sdks/ios-content) \| [Android](/sdks/android-content) \| [JavaScript](/sdks/js-content) \| [React Native](/sdks/react-native-content) \| [Flutter](/sdks/flutter-content) \| [Server API](https://docs.quickblox.com/reference/content) | Upload, download, delete media files such as photos, video, documents, etc. | ### Push notifications | Feature | Platforms | Description | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | Instant notifications | [iOS](/sdks/ios-push-notifications#send-apns--push-notifications) \| [Android](/sdks/android-push-notifications#send-push-notifications) \| [JavaScript](/sdks/js-push-notifications#send-push-notifications) \| [React Native](/sdks/react-native-push-notifications#send-push-notifications) \| [Flutter](/sdks/flutter-push-notifications#send-push-notifications) \| [Server API](https://docs.quickblox.com/reference/create-event) | Send instant push notifications to a user's device. | | Scheduled notifications | [iOS](/sdks/ios-push-notifications#send-apns--push-notifications) \| [Android](/sdks/android-push-notifications#send-push-notifications) \| [JavaScript](/sdks/js-push-notifications#send-push-notifications) \| [React Native](/sdks/react-native-push-notifications#send-push-notifications) \| [Flutter](/sdks/flutter-push-notifications#send-push-notifications) \| [Server API](https://docs.quickblox.com/reference/create-event) | Create push notifications that will be sent at a specific time/date. | | Recurring notifications | [iOS](/sdks/ios-push-notifications#send-apns--push-notifications) \| [Android](/sdks/android-push-notifications#send-push-notifications) \| [JavaScript](/sdks/js-push-notifications#send-push-notifications) \| [React Native](/sdks/react-native-push-notifications#send-push-notifications) \| [Flutter](/sdks/flutter-push-notifications#send-push-notifications) \| [Server API](https://docs.quickblox.com/reference/create-event) | Create push notifications that will be sent repeatedly according to the time interval. | | Notifications by user tags | Supported | Send push notifications to specific user groups defined by tags. | | Subscribe device | [iOS](/sdks/ios-push-notifications#subscribe) \| [Android](/sdks/android-push-notifications#automatic-push-subscription) \| [React Native](/sdks/react-native-push-notifications#subscribe-to-push-notifications) \| [Flutter](/sdks/flutter-push-notifications#create-push-subscription) \| [Server API](https://docs.quickblox.com/reference/create-subscription) | Subscribe/unsubscribe a device to receive push notifications. | ### Custom objects | Feature | Platforms | Description | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Data schema | [iOS](/sdks/ios-custom-objects) \| [Android](/sdks/android-custom-objects) \| [JavaScript](/sdks/js-custom-objects) \| [React Native](/sdks/react-native-custom-objects) \| [Flutter](/sdks/flutter-custom-objects) \| [Server API](https://docs.quickblox.com/reference/custom-objects) | Create a flexible data schema so you can store and structure your data exactly how you need to. | | Data management | [iOS](/sdks/ios-custom-objects) \| [Android](/sdks/android-custom-objects) \| [JavaScript](/sdks/js-custom-objects) \| [React Native](/sdks/react-native-custom-objects) \| [Flutter](/sdks/flutter-custom-objects) \| [Server API](https://docs.quickblox.com/reference/custom-objects) | Create, update, delete custom object records. | | Flexible queries | [iOS](/sdks/ios-custom-objects) \| [Android](/sdks/android-custom-objects) \| [JavaScript](/sdks/js-custom-objects) \| [React Native](/sdks/react-native-custom-objects) \| [Flutter](/sdks/flutter-custom-objects) \| [Server API](https://docs.quickblox.com/reference/list-records) | Define specific search criteria based on comparisons to the fields of each record and list the exact data that you need. | | Relations | [iOS](/sdks/ios-custom-objects#relations) \| [Android](/sdks/android-custom-objects#relations) \| [JavaScript](/sdks/js-custom-objects#relations) \| [Server API](https://docs.quickblox.com/reference/relations) | Build one-to-many relations between data schemas. | | Permissions | [iOS](/sdks/ios-custom-objects#permission-schema) \| [Android](/sdks/android-custom-objects#permission-schema) \| [JavaScript](/sdks/js-custom-objects#permission-schema) \| [Server API](https://docs.quickblox.com/reference/permissions) | Add a flexible permissions schema by choosing from five permission levels. Specify users who can access the object and operations allowed on a given object. | ### Address book | Feature | Platforms | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Contact management | [iOS](/sdks/ios-address-book) \| [Android](/sdks/android-address-book) \| [JavaScript](/sdks/js-address-book) \| [Server API](https://docs.quickblox.com/reference/address-book) | Upload contacts to the server. Keep the contacts up-to-date by updating, deleting or syncing the device contacts with QuickBlox. | | Retrieve registered users | [iOS](/sdks/ios-address-book#retrieve-registered-users) \| [Android](/sdks/android-address-book#retrieve-registered-users) \| [JavaScript](/sdks/js-address-book#retrieve-registered-users) \| [Server API](https://docs.quickblox.com/reference/get-registered-contacts-users) | Retrieve registered QuickBlox users from the address book. | | New contact notifications | [iOS](/sdks/ios-address-book#push-notification-on-new-contact-joined) \| [Android](/sdks/android-address-book#push-notification-on-new-contact-joined) \| [JavaScript](/sdks/js-address-book#push-notification-on-new-contact-joined) | Enable push notifications when a contact from your address book joins the app. | ## Getting around Our documentation is split into two basic sections: 1. **Documentation**. Here you can explore our SDKs in-depth. QuickBlox supports multiple platforms including iOS, Android, JavaScript, React Native, and Flutter. Learn all fundamentals for integrating and configuring our SDKs for each particular platform. 2. **Server API**. Use QuickBlox Server API to establish a server-to-server connection between your application and QuickBlox server and fulfill activities associated with chat. Our API documentation is organized around modules. Each module contains a set of specific API requests and respective endpoints. Find a list of parameters and their descriptions below each request. ## Basic recommendations Here are basic recommendations for navigating our documentation. 1. It’s better to start with our **Key Concepts** page to get an overall understanding of QuickBlox. 2. Take a look at our **Code Samples** page with a collection of ready-to-go apps for the most popular platforms. 3. Move over to the **Quick Start** guide of the platform you want to add the QuickBlox to. Learn how to run the code sample and send your first message from here. 4. Refer to our **Setup** page to learn how to initialize, connect, and configure QuickBlox SDK so it works seamlessly with your app. 5. Check **other pages** to customize QuickBlox further. Following other sections, you will understand how to implement each particular functionality in detail whether it is Chat, Video Calling, Push Notifications, etc. # Key Concepts Source: https://docs.quickblox.com/docs/key-concepts Learn about basic QuickBlox concepts to simplify the integration with QuickBlox. ## Dashboard QuickBlox Dashboard enables you to create new apps and manage your existing apps using Chat, Content, Push Notifications, Custom Objects, and Users modules. Get your application credentials (**Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key**) from here to launch SDKs. For backend integration use **API Keys**. ## Application QuickBlox application includes all you need to add real-time messaging and voice/video calling to your app including users, push notifications, file storage, etc. A user logs in to the app, reviews a list of dialogs, chooses a dialog, or creates their own, writes a message, and sends it to the dialog. Along with that, they can view chat history, configure pushes, and many more. Each QuickBlox application has a unique **Application ID** used for the integration with your app. Along with **Application ID** you get **Authorization Key**, **Authorization Secret**, and **Account Key** required for SDKs initialization, or **API Keys** for backend. These data are stored in the QuickBlox server database and can be subsequently identified on the server as a result of the successful initialization procedure. Keep in mind the points below: **All** users in your QuickBlox application can communicate across multiple platforms. This means your iOS, Android, or web users can easily connect with each other. Each QuickBlox application is tied to its own user database, ensuring that users are not mixed up between different applications. **Only** one QuickBlox application can be integrated into your app, meaning there's **no need** to create separate QuickBlox applications for each platform individually. ## Auth & API Keys | Type | Privileges | Recommended to use | | ---------------------------------------- | ----------------------------------------------- | -------------------------------------- | | Authorization Key + Authorization Secret | Allows to create application/user session token | In client side code during development | | API Key | Allows to perform actions on QuickBlox server | In server side code | ## Authentication QuickBlox API can be accessed either with **session** token or with **API key**. To access QuickBlox API a user needs to authenticate your application with the QuickBlox server. A token-based authentication is used to provide secure access to QuickBlox API. When the authenticity is verified, the server responds to the request with the access token that becomes a part of **each** subsequent request to the QuickBlox server. **QuickBlox does not handle user management.** QuickBlox offers only basic user management, for secure production implementation you must handle user registration and login at your end. Once the user is logged into your app/site, you can log in the user to QuickBlox **programmatically**. In this case the user does not ever directly login to QuickBlox. A **token** is a piece of data identifying a user or application and providing temporary access to QuickBlox Server API. The token is valid only within the session it was generated. The session gets expired in **2 hours** after the last request to the Server API. There are **two** types of tokens: **application session** token and **user session** token. The application session token is limited to read-only permissions preventing changes to application data while the user session token is not restricted in permissions and allows for making edits to application data. | Session Token Type | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Application session token | This kind of access token is needed to read the app data. Has only READ access to resources. The expiration time after the last Server API request is 2 hours. | | User session token | The user token is the most commonly used type of token. This kind of access token is needed any time the app calls an Server API to read, modify or write a specific user's data on their behalf. Has READ/WRITE access to resources. The expiration time after the last API request is 2 hours. | ## Users Users are managed via the User module. A user is the one who can access all chat features of a QuickBlox application with their own unique ID. QuickBlox server authorizes a user by the **user ID**. When the user sends a login request to the server, the server queries the database to check for a match upon the request for connection. A user can be assigned multiple **tags**. Set up user tags and address users separately in your app through the Server API or Dashboard. Tags can be managed via the user profile. There are two user roles in QuickBlox API: * **Account owner** is a user who has registered the application. A user with an administrator role who has access privileges that enable them to use all functions for application management, monitoring, and viewing information. This user can access each application of the account. The account owner is the one who signs up to QuickBlox Dashboard and can manage a Dashboard account. The account user is also an API user. * **Application user** is a user having the access to QuickBlox Server API and authorized to make requests to the QuickBlox server. An application user is the one who signs in to the QuickBlox application with their email, login, and password via the Server API. | Permission | Account owner | Application user | | ------------------ | ------------- | ---------------- | | Create user | + | + | | View user info | + | + | | Update themselves | + | + | | Update other users | + | - | | Delete themselves | + | + | | Delete other user | + | - | ## Chat QuickBlox Chat runs over the **XMPP** protocol. To be able to use it, you should establish a real-time connection with the QuickBlox server. If the connection to the server is lost, the application gets automatically connected to the Chat server in order to receive messages in real-time. **Dialogs** are the basis of chat activity. Users send messages to the dialog from where they get distributed to the opponent users. There are **3 dialog types**: * **Private**. A dialog for two participants. Created automatically upon the first message. * **Group**. A dialog involving more than two participants. To start a group dialog, the user should create a group dialog and add a new participant in the new group dialog. A user who is invited to the group dialog can either accept or decline the **invitation**. * **Public**. A dialog involving more than two participants. Any user can access this dialog **without** a preliminary **invitation**. **Messages** are data pools that get published in the dialogs. They can contain any data - arrays, objects, numbers, and strings. They also can acquire **read** and **delivered** statuses. There are supported **three** types of messages: * **Text**. By default, a message is considered as text. * **Media**. There are available any types of attachments, audio, video, image, or other. * **Custom**. A message can be extended with custom data such as location coordinates. Your application should be constantly connected to the Chat server to ensure that messages are always delivered efficiently. ## Video Calling In addition to messaging, QuickBlox allows you to add high-quality video calls to your apps. Our video calling API is built on [WebRTC principles](https://webrtc.org/) providing a simplified, secure communication channel including audio and video for web and mobile apps. The communication is happening between peers representing camera devices. There are two **peer types**: * **Local peer** is a device running the app right now. * **Remote peer** is an opponent device. Establishing real-time video communication between two peers involves 3 phases: * **Signaling**. At this phase, the peers’ local IPs and ports where they can be reached (ICE candidates) are exchanged as well as their media capabilities and session control messages. * **Discovery**. At this phase, the public IPs and ports at which endpoints can be reached are discovered by STUN/TURN server. * **Establishing a connection**. At this phase, the data are sent directly to each party of the communication process. The **signaling** is implemented over the **XMPP protocol** using QuickBlox Chat. Before starting to work with the Video Calling API, a **connection to the QuickBlox Chat** server is required to be established **first**. To call users, you should initiate a call. As a result, a **call session** is created. A call session represents a room for real-time audio/video, data, and/or screen-share session, and is the basic **building block** for your video calling app. To **receive events** associated with the call session and peer connection, the **event listener** is required to be set up. Thus, when your app gets disconnected from the server and tries to re-establish the XMPP connection with the QuickBlox server, the server notifies the app of the event through the event listener. Each particular audio/video call has a unique **session ID**. This allows you to have more than one independent audio/video calls. QuickBlox SDK supports **one-to-one calls** and **group calls** with up to four peers. If you want to make **group calls**, you can do it between a few users. Using WebRTC your device connects with each user in the call. It means that each device must handle as many connections as there are users in the current call. This table shows how many connections your device must establish to make a group call with a different number of users in the call. | Users in Call | Connections number | Per user | | ------------- | ------------------ | ------------- | | 2 | 2 connections | 2 connections | | 3 | 6 connections | 4 connections | | 4 | 12 connections | 6 connections | | 5 | 20 connections | 8 connections | To **make a call with 4 other** participants each device has to handle **8 different connections** to send and receive audio and video tracks to each other call participant. It requires high-performance devices. The **other solution** we provide requires only **1 output and 1 input** connection to make **up to 10 users** multi-user call. Please use this WebRTC Video Calling to make Group Calls with 4 or fewer users. Due to the Mesh architecture used for multi-point calls, where every participant sends and receives media from all others, the current solution supports group calls with up to 4 people. You can also set up a connection with up to 12 people in the call at the same time. This feature is available only for Enterprise plans. If you need to make group calls with more than 4 users, please contact us for more details on the feature. ## Video Conference QuickBlox provides a Multiparty Video Conferencing solution allowing to set up a video conference between **10-12** people. It is built on top of [WebRTC SFU](https://webrtcglossary.com/sfu/) technologies. To call users, you should create a conference **session**. To **process events** associated with the session state, the **event listener** is required to be implemented. Once a session is created, you can interact with the video conferencing API. Each particular conference session has a unique **session ID**. This allows you to have more than one independent conference calls. QuickBlox Video Conferencing API is based on top of regular **Video Calling API**, so we recommend getting familiar with it **first**. This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). ## Content With the QuickBlox content module, a user can store rich chat attachments, app content, and settings without having to re-publish it. A **file** is a dedicated entity within the system. It has a unique **file ID** that is generated automatically by the server upon file creating. The file gets uploaded to a cloud storage. After a file is declared as uploaded, a URL of the file is returned by the server. Following the URL, the file can be subsequently downloaded. There are two types of file URLs that can be obtained: * **Public URL** allows anyone to access the file, no authorization token is needed. * **Private URL** can be accessed only by the QuickBlox user with a session token. You can upload, delete, edit files as well as view the list of **all** available files via the Dashboard or Server API requests. ## Push Notifications Our SDK allows you to build an app that can receive push notifications. As a result, your users will always be informed with timely content even though the app is closed or running in the background. QuickBlox provides a way to set up **offline messaging** for your app. This means that a push notification will be automatically delivered to the opponent device only when it is fully offline. Push notifications for your apps are sent using **FCM** (Google's Firebase Cloud Messaging) or **APNs** (Apple Push Notification service). When a message is sent to the QuickBlox server through the SDK, the server communicates with APNs or FCM regarding the message. The logic is built around **two** main concepts: * **Subscription**. A device should be subscribed to APNs and FCM services. The subscription parameters (device id, private id, app id, notification channel, etc.) are sent by the QuickBlox server within a push subscription request to the Google server and Apple server. Once the device is subscribed for the services, the APNs or FCM delivers a notification to it. * **Event**. The event is a dedicated entity within the system. Each event has a unique event ID generated automatically by the server after event creation. Once the event is created, the server initiates sending a push notification. ## Custom Objects QuickBlox Custom Objects module represents a **key-value storage** enabling you to create **any** data structure using the Dashboard or Server API. There are **two** key concepts in Custom Objects: * **Class** represents your schema and contains field names and types. * **Record** represents data you put into your schema. You will be able to create classes containing different field types (integer, string, float, boolean, etc.) as well as **set relations** between different classes. You can also set **access control** for the class to define who can access the records in the class and make edits to it. Custom Objects module provides flexibility to define any data structure (schema) you need, build one-to-many relations between schemas and control permissions for all operations made on data. The schema is defined in QuickBlox Dashboard. There are two key concepts in Custom Objects: \- **Class** represents your schema and contains field names and types. \- **Record** represents data you put into your schema. **Class** and **Record** are similar to table and row in relational database. Every class in Custom Object module comes with five mandatory predefined fields: `_id`, `user_id`, `parent_id`, `created_at`, and `updated_at`. Allowed data types: **Integer** (or Array of Integer); **String** (or Array of String); **Float** (or Array of Float); **Boolean** (or Array of Boolean); **Location** (Array of `[, ]`); **File**; **Date**. For example, you want to store a simple calendar that stores the event title and event date. In this case, you can create a **Calendar** class with following fields: `event_title` and `event_date`. Calendar records will have the following structure: | Field name | Type | | ------------- | ------- | | \_id\* | String | | user\_id\* | Integer | | parent\_id\* | Integer | | event\_title | String | | event\_date | Date | | created\_at\* | Integer | | updated\_at\* | Integer | Calendar data will look like: | \_id\* | user\_id\* | parent\_id\* | event\_title | event\_date | created\_at\* | updated\_at\* | | ------------------------ | ---------- | ------------ | ---------------- | -------------------- | ------------- | ------------- | | 5d866b53a28f9a5ad51cb562 | 96753878 | null | Meet John | 2019-12-25T14:00:00Z | 1576629761 | 1576629761 | | 5d866b53a28f9a5ad51cb563 | 96753878 | null | Visit my dentist | 2019-09-25T15:00:00Z | 1577282400 | 1577282400 | ### Relations It is possible to create a relation between objects of two different classes via `_parent_id` field. For example, we have the class **Rating** that contains `score`, `review`, and `comment` fields. We also have a **Movie** class. So we can create a record of class **Rating** that will point to the record of the class **Movie** via its `_parent_id` field, so the `_parent_id` field will contain the ID of record from class **Movie**. This is not a simple soft link. This is actually a **hard link**. When you delete the **Movie** class record then all its children (records of class **Rating** with `_parent_id` field set to the **Movie** class record ID) will be automatically **deleted** as well. If you need to retrieve all children, you can retrieve records with the filter `_parent_id=`. ### Permissions Access control list available **only** for Custom Objects module. Access control list (ACL) is a list of permissions attached to some object. An ACL specifies which users have access to objects as well as what operations are allowed on given objects. Each entry in a typical ACL specifies a subject and an operation. ACL models may be applied to collections of objects as well as to individual entities within the system hierarchy. **Actions** Here are actions you can perform on record entity: | Action | Description | | ------ | -------------------------------------------------------------------------------------------------------------- | | Create | Create a record. | | Read | Retrieve and read the info about the chosen record. | | Update | Update any parameter for the chosen record (only those parameters that can be set by the user can be updated). | | Delete | Delete a record. | **Permission levels** QuickBlox permission schema contains **five** permissions levels: | Permission level | Description | | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Open (open) | Such permission schema means that any user within the application can access the record/records in the class and is allowed to perform an action with the current permission level. | | Owner (owner) | Owner permission level means that only Owner (a user who created a record) is allowed to perform action with the current permission level. | | Not allowed (not\_allowed) | No one (except for the Account Administrator) can make a chosen action. | | Open for groups (open\_for\_groups) | Users having a specified tag/tags (see more info about how to set tags for the user in [Users](https://docs.quickblox.com/reference/users) section) will be included in the group that is allowed to perform an action with the current permission level. The current permission level can consist of one or several groups (number of groups is **not** limited). Tags can be added/deleted in the user’s profile. | | Open for user ids (open\_for\_users\_ids) | Only users that are specified in the permission level can make a required action with a record. One or several users can be specified (the number of users is **not** limited). | # Quickstart Source: https://docs.quickblox.com/docs/quickstart QuickBlox provides powerful Chat API and SDK to add real-time messaging and video calls to your web and mobile apps. Learn how to integrate QuickBlox across multiple platforms. ## Have Questions? Need Support? Join the QuickBlox Developer Discord Community, where you can share ideas, learn about software, & get support.
[Join Discord](https://discord.gg/quickblox) An overview of QuickBlox platform features and documentation guidelines.
Learn more
An overview of QuickBlox concepts necessary to know to simplify integration.
Learn more
## SDKs and APIs Learn how to add QuickBlox to your iOS app and send your first message.
QuickStart
Download samples
View on GitHub
Learn how to add QuickBlox to your Android app and send your first message.
QuickStart
Download samples
View on GitHub
Learn how to add QuickBlox to your web app and send your first message.
QuickStart
Download samples
View on GitHub
Learn how to add QuickBlox to your React Native app and send your first message.
QuickStart
Download samples
View on GitHub
Learn how to add QuickBlox to your Flutter app and send your first message.
QuickStart
Learn how to add QuickBlox to your server app.
QuickStart
## UIKits Use pre-built UI elements for iOS to create a chat interface quickly and easily.
Overview
View on GitHub
Use pre-built UI elements for Android to create a chat interface quickly and easily.
Overview
View on GitHub
Use pre-built UI elements for React/Web to create a chat interface quickly and easily.
Overview
View on GitHub
Use pre-built UI elements for Flutter to create a chat interface quickly and easily.
Overview
View on GitHub
## AI Agent Learn how to manage your AI Agent.
Learn more
Learn how to use your AI Agent in your application.
Learn more
## Code samples

Select a language below to download a chat sample app and go over the relevant documentation.

Learn more

Select a language below to download a Chat UIKit sample app and go over the relevant documentation.

Learn more

Select a language below to download a chatbot sample app and go over the relevant documentation.

Learn more

Select a language below to download a push notification sample app and go over the relevant documentation.

Learn more

Select a language below to download a video calling sample app and go over the relevant documentation.

Learn more

Select a language below to download a video conference sample app and go over the relevant documentation.

Learn more
## Add-ons A step-by-step guide on how to moderate your chat messages.
Learn more
A step-by-step guide on how to log in to QuickBlox API using external authentication.
Learn more
# Sign Up Source: https://docs.quickblox.com/docs/sign-up Learn how to sign up for QuickBlox. From [https://admin.quickblox.com/signup](https://admin.quickblox.com/signup) sign up to create a free QuickBlox account. You can use this account to build unlimited apps. Just provide your name, user name, and email, or sign up using your GitHub or Google account. Once done, you can start creating your first app. When you first sign up with QuickBlox, you get an account. sign_up.png # Users Source: https://docs.quickblox.com/docs/users-dashboard Learn how to manage your users with QuickBlox. All your users are confined to a QuickBlox application. Thus, there is no conflict when the same user is created by another QuickBlox application. All users in your QuickBlox application can communicate across multiple platforms. In other words, your iOS, Android, or web users can communicate with each other. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## View list of users To list users, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Users** page. Here you will see a list of all existing application users. 2. Enter a user ID into the search to list a specific user. ## Create user There are two ways you can create users for your QuickBlox application: * Create users using [Server API](https://docs.quickblox.com/reference/create-user). * Create users in the QuickBlox Dashboard. To create an application user via the Dashboard, follow the steps below: 1. Navigate to **Dashboard => *YOUR\_APP* => Add new user** in the QuickBlox Dashboard. 2. Fill in the **Add user** profile info. Enter the email/login, password, and confirm the password for the new user account. These parameters are required for creating a user. 3. Click the **Add user** button. Similarly, you can create more users in the Dashboard. You can create a read-only admin user. The read-only admin user is a user with an administrator role who has view access to most data in the Dashboard. They are unable to edit data. To grant a read-only access to a user, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Users** in the QuickBlox Dashboard. 2. Find a user you want to grant a read-only access to. 3. Check the **Is admin (read only)**. 4. Click the **Save and close** button. ## Update user To update a user, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Users** page. Here you will see a list of all existing application users. 2. Choose the ID of the user you want to update and click on it. 3. Edit the profile of the user. 4. Click the **Save and close** button to save changes. Confirmed addresses cannot be updated right away. As soon as the email is confirmed, it is updated. See [this section](/docs/users-dashboard#email-confirmation) to learn more about email confirmation. ## Delete user To delete a user, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => Users** page. Here you will see a list of all existing application users. 2. Check the user you need to delete. 3. Click the **Remove selected**. When the account owner deletes a user, all data associated with the user are deleted from the system. Thus: 1. All files associated with the user are deleted. 2. All custom objects associated with the user are deleted. 3. All dialogs where the user is a dialog owner are deleted. If a user is a dialog occupant and not a dialog owner, their ID is deleted from the occupants IDs list of all group dialogs they participate in. However, a user isn't removed from occupants IDs list of private dialogs. ## Configure email notifications You can set templates of email notifications that will be sent to your users. You can use the user-specific tags described below: User: * `user.full_name` * `user.email` * `user.login` * `user.phone` * `user.website` * `user.created_at` * `user.updated_at` * `user.last_request_at` * `user.external_user_id` * `user.facebook_id` * `user.twitter_id` * `user.blob_id` * `user.get_reset_password_token` * `user.get_confirmation_token` * `user.show_name` App: * `app.title` * `app.url` * `app.description` Make sure to enable the [email confirmation](/docs/users-dashboard#email-confirmation). This functionality allows application users to confirm their emails. If a user doesn't confirm the email, the emails won't be sent to this user. HTML `` tag would be present in email subject. If you want to use your custom template, be aware that many mail servers disallow most of standard HTML tags. </Note> ### Greetings template You can set a 'Greetings' template of email notifications that will be sent to your app users when they sign up. To set the 'Greetings' email, follow the steps below: 1. Enable the email confirmation functionality. See [this section](/docs/users-dashboard#email-confirmation) for more details. 2. Navigate to the **Dashboard => *YOUR\_APP* => Users => Email Notifications** page. 3. Check the **Enable 'Greetings' email** template. 4. Choose the **Greetings email** from the dropdown. 5. Create an email template using the supported tags listed above. 6. Click the **Save changes** button. You can also click the **Save and preview** or **Save and send test** email option to preview or test the email template. <Note> Note that many mail servers block some standard HTML tags. </Note> <Frame> <img /> </Frame> <Warning> If the **Enable 'Greetings' email** is not checked, the users won't receive any emails. </Warning> ### Reset password template You can set the 'Reset password' template of email notifications that will be sent to your app users who want to reset their passwords. To set the 'Reset password' template, follow the steps below: 1. Enable the email confirmation functionality. See [this section](/docs/users-dashboard#email-confirmation) for more details. 2. Navigate to **Dashboard => *YOUR\_APP* => Users => Email Notifications** page. 3. Check the **Enable custom 'Reset password' email** template. 4. Choose the **Password reset email** from the dropdown. 5. Create an email template using the supported tags listed above. Add the `#{user.get_reset_password_token}` to reset password link to the template. For example, ` https://api.quickblox.com/password_reset?token=#{user.get_reset_password_token}`. Thus, when the user clicks on the emailed link, the token is going to be sent back to the application as part of the URL. 6. Click the **Save changes** button. You can also click the **Save and preview** or **Save and send test** email option to preview or test the email template. <Frame> <img /> </Frame> <Warning> If the **Enable custom 'Reset password' email** is not checked, the users will receive a default QuickBlox email. </Warning> <Note> You can also use your own server/page to perform the password reset operation. In this case, your web page should perform a request to QuickBlox server to save new password: `PUT https://api.quickblox.com/update_password.json`. Pass the following parameters within the request body: * `user[reset_password_token]` * `user[password]` * `user[password_confirmation]` </Note> ### Email confirmation template You can set the 'Email confirmation' template of email notifications that will be sent to users when they sign up. To set the 'Email confirmation' template, follow the steps below: 1. Enable the email confirmation functionality. See [this section](/docs/users-dashboard#email-confirmation) for more details. 2. Navigate to the **Dashboard => *YOUR\_APP* => Users => Email Notifications** page. 3. Check the **Enable custom 'Email confirmation' email** template. 4. Choose the **Confirmation email** from the dropdown. 5. Create an email template using the supported tags listed above. Add the `#{user.get_confirmation_token}` to the email confirmation link in the template. For example, `https://api.quickblox.com/confirm_email?token=#{user.get_confirmation_token}`. Thus, when the user clicks on the emailed link, the token is going to be sent back to the application as part of the URL. 6. Click the **Save changes** button. You can also click the **Save and preview** or **Save and send test** email option to preview or test the email template. <Frame> <img /> </Frame> <Warning> If the **Enable custom 'Email confirmation' email** is not checked, the users will receive a default QuickBlox email. </Warning> ## Configure user settings You can set the following settings for your users: ### Email confirmation The functionality enables application users to confirm their emails. Make sure to enable the email confirmation. To enable the email confirmation, proceed as follows: 1. Go to the **Dashboard => *YOUR\_APP* => Users => Settings => User registration confirmation** and check the box. 2. Click the **Save** button. <Frame> <img alt="user registration.png" /> </Frame> <Warning> If a user doesn't confirm the email, the emails won't be sent to this user. As a result, password reset functionality won't work. </Warning> ### New contact joined from address book notifications There is a way to get a push notification when some contact from your address book has been registered in the app. You can enable this feature at QuickBlox Dashboard as follows: 1. Follow the **Dashboard => *YOUR\_APP* => Users => Settings** direction 2. Enable push notifications for new contacts. 3. Click the **Save** button. <Frame> <img alt="new contact joined.png" /> </Frame> # Add Reaction Source: https://docs.quickblox.com/reference/add-reaction POST https://api.quickblox.com/chat/Message/{chat_message_id}/reactions Add a reaction to a message. <br/> *Added in v2.58.0* #### Recipes <AccordionGroup> <Accordion title="Add a reaction to message"> <Steps> <Step title="1. Set the message ID"> Provide the `message_id` of the message you want to react to. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="2. Set the reaction type"> Provide a valid `name` of the reaction, such as `"like"`, `"love"`, or `"haha"`. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="3. As a result, the API returns a '200 OK' status code, indicating that the reaction was added."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> </Steps> </Accordion> <Accordion title="Add a reaction to message with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="3. Set the message ID"> Provide the `message_id` of the message you want to react to. ```curl {7} theme={null} curl -X POST \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="4. Set the reaction type"> Provide a valid `name` of the reaction, such as `"like"`, `"love"`, or `"haha"`. ```curl {5} theme={null} curl -X POST \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="5. As a result, the API returns a '200 OK' status code, indicating that the reaction was added."> ```curl theme={null} curl -X POST \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> </Steps> </Accordion> </AccordionGroup> **Duplicate Reactions** If the user has already added the same reaction to the message, the request will be silently ignored and treated as successful. No duplicate reactions will be created. **Reaction Limits** * **Per User Per Message:** A user can add up to **20 different reactions** to a single message. * **Per Message (All Users):** A message can have up to **20 distinct reaction types** total. **Public Dialog Restriction** Reactions are not supported for messages in `type=1` (PUBLIC) dialog. If you attempt to add a reaction to a message in a `type=1` (PUBLIC) dialog, the request will fail with an error. #### Path Parameters <ParamField type="string"> ID of the message. Generated automatically by the server after message creation. </ParamField> #### Body Parameters <ParamField type="string"> The type of reaction to add. Examples: `"like"`, `"love"`, `"haha"`, `"angry"`. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> Reaction successfully added </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "name":"like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 400 theme={null} // Maximum reaction types for this message reached (20). { "errors": [ "This message has reached the maximum number of allowed reaction types (20). No additional reaction types can be added." ] } // Maximum reactions per message reached (20). { "errors": [ "You have reached the maximum number of reactions (20) allowed per message." ] } // Missing or invalid "name" parameter. { "errors": [ "Invalid or missing \"name\" parameter." ] } // Missing or invalid "message_id" parameter. { "errors": [ "Invalid or missing \"message_id\" parameter." ] } ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to perform this operation" ] } ``` ```json 404 theme={null} { "errors": [ "Message not found for the specified user and message ID." ] } ``` ```json 422 theme={null} { "errors": [ "Reactions are not allowed in public dialogs." ] } ``` </ResponseExample> # Address Book Source: https://docs.quickblox.com/reference/address-book Address Book API allows you to keep your contacts in sync across all devices or individual for each device. # AI Source: https://docs.quickblox.com/reference/ai AI API allows you to use QuickBlox AI features. # AI Answer Assist Source: https://docs.quickblox.com/reference/ai-extensions-ai-answer-assist POST https://api.quickblox.com/ai/ai_extensions/ai_answer_assist <Warning> Before call make sure that AI Answer Assist feature enabled on admin dashboard, otherwise you will get 405 error. </Warning> #### Body Parameters <ParamField type="string"> AI Agent id. </ParamField> <ParamField type="string"> Message you want to get answer for. The maximum length for the message is 2000 symbols. </ParamField> <ParamField type="array"> Conversation history. Used to add context. <Expandable title="Object"> <ParamField type="string"> May be user or assistant. </ParamField> <ParamField type="string"> Text message. </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="answer" type="string" /> </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="405"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "smart_chat_assistant_id": "65d5f3473d5d68095462165d", "message": "Who are you?", "history": [ { "role": "assistant", "message": "History message" }, { "role": "user", "message": "Hi" }, { "role": "assistant", "message": "Hello! How can I assist you today?" } ] }' \ https://api.quickblox.com/ai/ai_extensions/ai_answer_assist ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "answer": "Hello! How can I assist you today?" } ``` ```json 400 theme={null} { "errors": [ "Validation Error: history: Invalid history data, must be an array of objects with keys: role(must be user or assistant) and message(string). For example:\n{\"history\":[{\"role\":\"user\", \"message\":\"Some message\"}]}" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` ```json 405 theme={null} { "errors": [ "AI Answer Assistant is disabled" ] } ``` </ResponseExample> # HIPAA AI Gateway Source: https://docs.quickblox.com/reference/ai-extensions-ai-gateway POST https://api.quickblox.com/ai/ai_extensions/ai_gateway <Warning> Before call make sure that HIPAA AI Gateway feature enabled on QuickBlox Dashboard, otherwise you will get 405 error. To obtain HIPAA AI Gateway feature please contact us by [enterprise@quickblox.com](mail:enterprise@quickblox.com). </Warning> <Note> Accepted image types: jpeg, png. Max image size: 50MB. </Note> #### Recipes <AccordionGroup> <Accordion title="Create chat completion"> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "smart_chat_assistant_id": "65d5f3473d5d68095462165d", "messages": [ { "role": "developer", "content": "You are helpful assistant" }, { "role": "user", "content": "Hi" } ] }' \ https://api.quickblox.com/ai/ai_extensions/ai_gateway ``` </Accordion> <Accordion title="Analize image with URL"> <Note> Accepted image types: jpeg, png. Max image size: 50MB. </Note> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "smart_chat_assistant_id": "65d5f3473d5d68095462165d", "messages": [ { "role": "developer", "content": "You are helpful assistant" }, { "role": "user", "content": [ { "type": "text", "text": "What's in this image?" }, { "type": "image_url", "image_url": { "url": "https://some_website/image.jpg" } } ] } ] }' \ https://api.quickblox.com/ai/ai_extensions/ai_gateway ``` </Accordion> <Accordion title="Analize image with Base64 encoded image"> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "smart_chat_assistant_id": "65d5f3473d5d68095462165d", "messages": [ { "role": "developer", "content": "You are helpful assistant" }, { "role": "user", "content": [ { "type": "text", "text": "What's in this image?" }, { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAYGBgYHBgcICAcKCwoLCg8ODAwODxYQERAREBYiFRkVFRkVIh4kHhweJB42KiYmKjY…" } } ] } ] }' \ https://api.quickblox.com/ai/ai_extensions/ai_gateway ``` </Accordion> </AccordionGroup> #### Body Parameters <ParamField type="string"> AI Agent ID. </ParamField> <ParamField type="array"> Messages in OpenAI format. Please refer to OpenAI documentation [OpenAI messaging documentation](https://platform.openai.com/docs/guides/text?api-mode=chat#message-roles-and-instruction-following), [OpenAI analyze images documentation](https://platform.openai.com/docs/guides/images-vision?api-mode=chat#analyze-images). <Expandable title="Message"> <ParamField type="string"> May be user, assistant, or developer. </ParamField> <ParamField type="string or array"> Text content `string` Array of content parts `array` <Expandable title="Text content part"> <ParamField type="string"> `text` </ParamField> <ParamField type="string"> Text message </ParamField> </Expandable> <Expandable title="Image content part"> <ParamField type="string"> `image_url` </ParamField> <ParamField type="object"> <Expandable title="Image url"> <ParamField type="string"> Image url or Base64 encoded image. </ParamField> </Expandable> </ParamField> </Expandable> </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="answer" type="string" /> </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="405"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "smart_chat_assistant_id": "65d5f3473d5d68095462165d", "messages": [ { "role": "developer", "content": "You are helpful assistant" }, { "role": "user", "content": "Hi" } ] }' \ https://api.quickblox.com/ai/ai_extensions/ai_gateway ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "answer": "Hello! How can I assist you today?" } ``` ```json 400 theme={null} { "errors": [ "Failed to process AI Gateway request" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` ```json 405 theme={null} { "errors": [ "AI Gateway is disabled" ] } ``` </ResponseExample> # AI Summarize Source: https://docs.quickblox.com/reference/ai-extensions-ai-summarize POST https://api.quickblox.com/ai/ai_extensions/ai_summarize <Warning> Before call make sure that AI Summarize feature enabled on admin dashboard, otherwise you will get 405 error. </Warning> <Note> A summary will be generated using the latest 1,000 messages from the conversation. </Note> <Note> Please note that if there are no messages in dialog you will receive next response: ```json theme={null} 200 { "summary": "Chat is empty." } ``` </Note> #### Body Parameters <ParamField type="string"> AI Agent ID. </ParamField> <ParamField type="string"> ID of the dialog that will be summarized. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="summary" type="string" /> </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="405"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "smart_chat_assistant_id": "65d5f3473d5d68095462165d", "dialog_id": "65d5f3473d5d68095462166a" }' \ https://api.quickblox.com/ai/ai_extensions/ai_summarize ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "summary": "The user contacted support to clarify a billing discrepancy they noticed in their latest invoice. They explained that they were charged for a premium subscription despite downgrading to the free plan last month. The assistant verified the account details, confirmed the downgrade request had not been processed, and issued a refund. The conversation then shifted to questions about using the mobile app’s new features, particularly exporting reports and setting up daily usage notifications. The assistant provided step-by-step guidance and shared relevant help center links. The user confirmed the issues were resolved and expressed satisfaction with the support received." } ``` ```json 400 theme={null} { "errors": [ "Validation Error: dialog_id: Dialog id can't be empty, must be a string" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` ```json 405 theme={null} { "errors": [ "AI Summarize is disabled" ] } ``` </ResponseExample> # AI Translate Source: https://docs.quickblox.com/reference/ai-extensions-ai-translate POST https://api.quickblox.com/ai/ai_extensions/ai_translate <Warning> Before call make sure that AI Translate feature enabled on admin dashboard, otherwise you will get 405 error. </Warning> Supported language codes: | Language | Code | | ------------------- | ------- | | English | en | | Spanish | es | | Chinese simplified | zh-Hans | | Chinese traditional | zh-Hant | | French | fr | | German | de | | Japanese | ja | | Korean | ko | | Italian | it | | Russian | ru | | Portuguese | pt | | Arabic | ar | | Hindi | hi | | Turkish | tr | | Dutch | nl | | Polish | pl | | Ukrainian | uk | | Albanian | sq | | Armenian | hy | | Azerbaijani | az | | Basque | eu | | Belarusian | be | | Bengali | bn | | Bosnian | bs | | Bulgarian | bg | | Catalan | ca | | Croatian | hr | | Czech | cs | | Danish | da | | Estonian | et | | Finnish | fi | | Galician | gl | | Georgian | ka | | Greek | el | | Gujarati | gu | | Hungarian | hu | | Indonesian | id | | Irish | ga | | Kannada | kn | | Kazakh | kk | | Latvian | lv | | Lithuanian | lt | | Macedonian | mk | | Malay | ms | | Maltese | mt | | Mongolian | mn | | Nepali | ne | | Norwegian | no | | Pashto | ps | | Persian | fa | | Punjabi | pa | | Romanian | ro | | Sanskrit | sa | | Serbian | sr | | Sindhi | sd | | Sinhala | si | | Slovak | sk | | Slovenian | ur | | Uzbek | uz | | Vietnamese | vi | | Welsh | cy | #### Body Params <ParamField type="string"> AI Agent id. </ParamField> <ParamField type="string"> Text to translate. The maximum length for the text is 2000 symbols. </ParamField> <ParamField type="string"> Translation language code. Defaults to `en`. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="answer" type="string" /> </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="405"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "smart_chat_assistant_id": "65d5f3473d5d68095462165d", "text": "Hola", "to_language": "en" }' \ https://api.quickblox.com/ai/ai_extensions/ai_translate ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "answer": "Hello" } ``` ```json 400 theme={null} { "errors": [ "Validation Error: to_language: Wrong language code, available languages: en, es, zh-Hans, zh-Hant, fr, de, ja, ko, it, ru, pt, ar, hi, tr, nl, pl, uk, sq, hy, az, eu, be, bn, bs, bg, ca, hr, cs, da, et, fi, gl, ka, el, gu, hu, id, ga, kn, kk, lv, lt, mk, ms, mt, mn, ne, no, ps, fa, pa, ro, sa, sr, sd, si, sk, sl, ur, uz, vi, cy" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` ```json 405 theme={null} { "errors": [ "AiTranslate is disabled" ] } ``` </ResponseExample> # Authentication Source: https://docs.quickblox.com/reference/authentication QuickBlox API can be accessed either with **session** token or with **API key**. <Warning> An **API key** is exclusively designed for server API requests and not work with SDK implementations. It is intended for direct server-to-server communication and might cause errors or unexpected behavior if used within an SDK environment. </Warning> To access QuickBlox API you need to authenticate your application with QuickBlox server. A token-based authentication should be used to provide secure access to QuickBlox API. The token-based authentication works by ensuring that each request to the server contains a token that is verified by the server for authenticity. When the authenticity is verified, the server responds to the request. ### API key An **API key** is designed for server-to-server communication and should not be shared with or used by end-users directly. It serves as a secure credential to authenticate and authorize requests made to the server API. **Key Features:** * The API key is permanent and does not have an expiration date. * It grants full permissions to perform any action within the server API. **Obtaining an API Key** To obtain an API key, please follow the instructions [Create API key](/docs/application#create-api-key) **API Key Usage** 1. To authenticate requests using the API key, include it in the request headers as follows the example: <CodeGroup> ```bash cURL theme={null} curl -X GET \ -H 'Authorization: ApiKey {API_KEY}' \ https://api.quickblox.com/users ``` </CodeGroup> Replace `{API_KEY}` with the actual API key value. 2. Request Execution on Behalf of a User To execute a request on behalf of a specific user, include the "On-Behalf-Of" header in your request. This allows the server to identify the user on whose behalf the request is being made. Example Request Header: <CodeGroup> ```bash cURL theme={null} curl -X GET -H 'Authorization: ApiKey {API_KEY}' \ -H 'On-Behalf-Of: {USER_ID}' \ https://api.quickblox.com/users ``` </CodeGroup> Replace `{USER_ID}` with the unique identifier of the QuickBlox user. **Security Considerations** To maintain the security and integrity of the API key, follow these best practices: * Keep the API key confidential and do not share it with unauthorized individuals. * Transmit the API key securely over HTTPS. * Regularly review and audit the server-side access and permissions associated with the API key. If an API key is compromised or suspected of being compromised, replace the API key immediately for further action. ### Application vs User session token A **token** is a piece of data identifying a user or application and providing temporary access to QuickBlox Server API. The **token** is valid only within the **session** it was generated. The **session** gets expired in 2 hours after the last request to the Server API. A session **token** is obtained via **Create Session** request having `/session.json` endpoint. Once a **token** is received, it becomes a part of each request and is placed within its header. There are two types of tokens: **application** session token and **user** session token. The **application** session token is limited to read-only permissions preventing changes to application data while the **user** session token is not restricted in permissions and allows for making edits to application data. | Session Token Type | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Application session token | This kind of access token is needed to read the app data. Has only READ access to resources. The expiration time after the last Server API request is 2 hours. | | User session token | The user token is the most commonly used type of token. This kind of access token is needed any time the app calls a Server API to read, modify or write a specific user's data on their behalf. Has READ/WRITE access to resources. The expiration time after the last Server API request is 2 hours. | <Warning> If you perform a query with an **expired token**, you receive the error `Required session does not exist`. In this case, you have to recreate a session token. </Warning> <Info> Want to change session **expiration time**? This option is available for [Enterprise Plan](https://quickblox.com/pricing/) only. [Contact the sales team](https://quickblox.com/enterprise/#get). </Info> #### Signature generation Use a signature mechanism to verify the contents of the [Create Session](/reference/create-session) request on the QuickBlox server. The signature is generated using the HMAC-SHA cryptographic hashing algorithm. Then it is sent in the session creation request to verify the end-user. The inputs to a hashing algorithm include: * A normalized string. The string is formed using the `parameter=value` pairs that should be sorted alphabetically by their text value and separated by the `&` character. Use the parameters of the create session request to form the string. * An authorization secret. The secret is known as a cryptographic key and is used to compute the HMAC. * A hashing algorithm. The cryptographic hash function. <Info> ### For Basic, Startup, Growth plan, and HIPAA Cloud users The SHA1 hash algorithm is used to generate a signature. </Info> <Info> ### For Basic, Startup, Growth plan, and HIPAA Cloud users * **By default**, the SHA1 hash algorithm is used to generate a signature. * [Contact our sales team](https://quickblox.com/enterprise/) if you need to change SHA1 to SHA256. </Info> **Signature generation examples** A signature generation request body is different for each authentication option. See the signature generation examples below: <CodeGroup> ```text Application session theme={null} REQUEST PARAMETERS: application_id auth_key nonce timestamp NORMALIZED STRING: application_id=716730&auth_key=bbfeCwWtz8dqF4F&nonce=33432×tamp=1572434294 AUTHORIZATION SECRET(SECRET KEY): YYXAU8BEYBfv0Fn RESULTING SIGNATURE: be6e49d06389a59d8846edf04789bcd7ada23eb8 ``` ```text User session (with login/password) theme={null} REQUEST PARAMETERS: application_id auth_key nonce timestamp user[login] user[password] NORMALIZED STRING: application_id=716730&auth_key=bbfeCwWtz8dqF4F&nonce=33431×tamp=1572434594&user[login]=amigo30&user[password]=amigo30pass AUTHORIZATION SECRET(SECRET KEY): YYXAU8BEYBfv0Fn RESULTING SIGNATURE: 99dc8e0a81afc0ff19b509c229c0256d7fe13220 ``` ```text User session (with Facebook) theme={null} REQUEST PARAMETERS: application_id auth_key keys[token] nonce provider timestamp NORMALIZED STRING: application_id=716730&auth_key=bbfeCwWtz8dqF4F&keys[token]=AM46dxjhisdffgry26282352fdusdfusdfgsdf&nonce=33432&provider=facebook×tamp=1572434694 AUTHORIZATION SECRET(SECRET KEY): YYXAU8BEYBfv0Fn RESULTING SIGNATURE: a54e6693b04f9edf6b42c903923f8e204e5316e6 ``` ```text User session (with Firebase) theme={null} REQUEST PARAMETERS: application_id auth_key firebase_phone[access_token] firebase_phone[project_id] nonce timestamp NORMALIZED STRING: application_id=86716&auth_key=XHudVt3ktSV3mpK&firebase_phone[access_token]=eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc3MTBiMDE3ZmQ5YjcxMWUwMDljNmMzNmIwNzNiOGE2N2NiNjgyMTEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcXVldWUtZGVtby1jbGllbnQiLCJhdWQiOiJxdWV1ZS1kZW1vLWNsaWVudCIsImF1dGhfdGltZSI6MTYyNjQzMjg5OSwidXNlcl9pZCI6InFhZnNjMHpnOHNVSktvdFJkb1J1eUx1UXdiRjMiLCJzdWIiOiJxYWZzYzB6ZzhzVUpLb3RSZG9SdXlMdVF3YkYzIiwiaWF0IjoxNjI2NDMyOTAwLCJleHAiOjE2MjY0MzY1MDAsInBob25lX251bWJlciI6IiszODA5Mzc2MzcyNTMiLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7InBob25lIjpbIiszODA5Mzc2MzcyNTMiXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwaG9uZSJ9fQ.ORDJTSGTicsvAeJ-eUQcDvt5Jv-y1c3HteOlzLApTZZ2C-nJZOB4RFpvoviIQwH53PFANB5iXTHBw1a5XBKROeRYEyu8YLg6RsH0wt5f5_hzGsUisgeFleDrz9i3V7NqkITqz2ADcJrWzwF-YDj5TbzCTlHmgeciiuViVbLqyFSKfyYrvFjM7x1IvJYUNVD3XGIq33128eaqHRuWfiaW5PC9ZdvfdOF5UhNpBx330TfM1FV9gNBQxd3cmxCE-bD1kvHQWLk6z8XKTNWZag718_Gf7MVmhqk-bC_KtwQ7IGVMRjPgCdNbA8H1btIcofD2iUVtzTew4et7leR6ujCAcA&firebase_phone[project_id]=56783&nonce=9823&provider=firebase_phone×tamp=1626433392 AUTHORIZATION SECRET(SECRET KEY): dMFLtwFt4fX567U RESULTING SIGNATURE: 3fc62b95b4689e5b619bc6f4adfa267f68bc06fc ``` </CodeGroup> <Warning> The request body parameters should be sorted **alphabetically** by their text value and separated with the `&` symbol. </Warning> **Signature generation with third-party tool** You can test signature generation algorithm with the HMAC-SHA generator. Thus, to generate the signature, proceed as follows: 1. Open the HMAC-SHA generator. For example, you can use [this one](https://www.freeformatter.com/hmac-generator.html). 2. Compose a normalized string from `parameter=value` pairs. For example: application\_id=716730\&auth\_key=bbfeCwWtz8dqF4F\&nonce=33432×tamp=1572434294 3. Enter the authorization secret(secret key). 4. Choose SHA1 hashing algorithm. 5. Generate the signature. Your screen should look similar to the screenshot below: <Frame> <img alt="signature generation" /> </Frame> # Blob Model Source: https://docs.quickblox.com/reference/blob-model | Property name | Type | Description | | ------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------ | | id | int | ID of the file/blob. Generated automatically by the server after file/blob creation. | | uid | string | File/blob identifier that should be taken from the response of the Create File request. | | content\_type | string | MIME content type. | | name | string | File/blob name. | | size | int | File/blob size. | | created\_at | datetime | Date & time when a record was created, filled automatically. | | updated\_at | datetime | Date & time when a record was updated, filled automatically. | | blob\_status | string | Current file/blob status. | | set\_completed\_at | timestamp | Date & time when file uploading was marked as completed. | | public | boolean | File/blob visibility. If the file is public then it's possible to download it without a session token. Default: `false`. | # Chat Source: https://docs.quickblox.com/reference/chat Chat module is a quick and reliable solution that combines benefits of the scalable cloud-hosted XMPP chat server, seamless Single Sign-On authorization via Users, incoming IM/chat alerts via Push Notifications and file attachments via Content. Chat Server API enables users to send messages and manage dialogs as well as provides access to chat history. # Class Model Source: https://docs.quickblox.com/reference/class-model | Field name | Type | Description | | ------------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------- | | \_id | string | ID of the class. Generated automatically by the server after class creation. | | application\_id | int | ID of the application. | | user\_id | int | ID of the user who created the class. | | name | string | Name of the class. | | custom\_fields | array of objects | Array of custom fields. | | custom\_fields.name | string | Name of a custom field. | | custom\_fields.type | string | Type of a custom field. Possible values are: `Integer`, `Float`, `String`, `Boolean`, `File`, `Date`, `Location`. | | custom\_fields.is\_array | string | Flag which identifies if a custom field has array type. Possible values are: `is_array=0`, `is_array=1`. | | acl | nested object | Access control list, a list of permissions attached to some object ([permissions](/reference/permissions)). | | acl.read | nested object | Retrieve and read the info about the chosen record permission. | | acl.update | nested object | Update record params permission. | | acl.delete | nested object | Delete records permission. | | acl.create | nested object | Create records permission. | <Warning> You can create a **Class** via API **only** for the **Account owner** token, not for a regular API user. </Warning> # Contact Model Source: https://docs.quickblox.com/reference/contact-model | Property name | Type | Description | | ------------- | ------ | -------------- | | name | string | Contact phone. | | phone | string | Contact name. | # Content Source: https://docs.quickblox.com/reference/content Content module allows storing rich chat attachments, app content, and settings without having to re-publish them. Using a web interface you or your clients can control and make instant changes to the apps. There is a dedicated entity for a **file** within the system - **blob**. **Files** and **blobs** are basically the **same**. * **Rich attachments.** Store chat rich attachments. * **File storage.** Store photos, videos, documents etc. * **Content management.** Manage text, rich text, and media via easy to use Dashboard. * **Synchronization.** App synchronizes content in background, users not affected. To **upload a file** you need to do the following: <CardGroup> <Card title="Create File" icon="file" href="/reference/create-file"> Make sure to create an entity for a file within the system - a blob, **before** uploading it. </Card> <Card title="Upload File" icon="file-arrow-up" href="/reference/upload-file"> to the QuickBlox cloud storage. An upload link is obtained from the response to the create file request. The link has expiration time so if it gets expired, you should obtain a new link using a create file request. </Card> </CardGroup> To **download a file** you need to do the following: <CardGroup> <Card title="Get File" icon="square-1" href="/reference/get-file-by-id" /> <Card title="Download File" icon="square-2" href="/reference/download-file" /> </CardGroup> # Create Child Record Source: https://docs.quickblox.com/reference/create-child-record POST https://api.quickblox.com/data/{class_name}/{parent_id}/{child_name} Create a child record. #### Recipes <AccordionGroup> <Accordion title="Create child record with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name": "Bob" }'\ https://api.quikblox.com/data/user/617817b94dd9626e64022fdd/friends.json ``` </Step> <Step title="2. Set record fields."> You can set as many fields as you need for a new child record. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name": "Bob" }'\ https://api.quikblox.com/data/user/617817b94dd9626e64022fdd/friends.json ``` </Step> <Step title="3. Set a parent class, parent record ID, and child class name."> Specify the parent class name, parent record ID, and child class name in the URL. Here, the parent class is `user`, the parent record ID is `617817b94dd9626e64022fdd`, and the child class is `friends`. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name": "Bob" }'\ https://api.quikblox.com/data/user/617817b94dd9626e64022fdd/friends.json ``` </Step> <Step title="4. As a result, the API returns a newly created child record."> ```json theme={null} { "_id": "617819e24dd96270083a270c", "_parent_id": "617817b94dd9626e64022fdd", "created_at": 1635260898, "name": "Bob", "updated_at": 1635260898, "user_id": 579, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> </AccordionGroup> A child record type is a record that refers to another record that has already been created, a parent. When a record is created, it is assigned a record ID. Thus, you can create a new record, a child, and attach it to the parent record by pointing to its ID. #### Path Parameters <ParamField type="string"> A class name of the parent custom object. </ParamField> <ParamField type="string"> An ID of the parent custom object. </ParamField> <ParamField type="string"> A class name of the child custom object. </ParamField> #### Body Parameters <ParamField type="string"> Set value to the field defined in Custom Object class. Can be many 1..N. </ParamField> <ParamField type="string"> Record permissions. Format: <br /> `permission.<CRUD_operation>.access=<value>` <br /> `permission.<CRUD_operation>.<option>=<value>` CRUD operations: `create`, `read`, `update`, `delete`. <br /> CRUD access values: `open`, `owner`, `open_for_users_ids`, `open_for_groups`. <br /> CRUD options: `ids`, `groups`. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="_id" type="string" /> <ResponseField name="_parent_id" type="string" /> <ResponseField name="created_at" type="integer" /> <ResponseField name="updated_at" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="permissions" type="object"> <Expandable title="properties"> <ResponseField name="read" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="update" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="delete" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 00b4daecf2f3cc13687515977f81e3613d000335" \ -d '{ "name": "Bob" }'\ https://api.quikblox.com/data/user/617817b94dd9626e64022fdd/friends.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "_id": "617819e24dd96270083a270c", "_parent_id": "617817b94dd9626e64022fdd", "created_at": 1635260898, "name": "Bob", "updated_at": 1635260898, "user_id": 579, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` ```json 400 theme={null} {} ``` </ResponseExample> # Create Class Source: https://docs.quickblox.com/reference/create-class POST https://api.quickblox.com/class.json Create a class to define any data structure you need. The class can be created by the account owner only. #### Recipes <AccordionGroup> <Accordion title="Create class"> <Steps> <Step title="1. Set a name of the classs"> Use a `name` parameter to set the name of the class. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: c95dbdc9c72cc8e7f2367d7ef2c6d99c5e013233" \ -d '{ "name":"UserCustomProfile", }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="2. Use a `fields` parameter to set custom fields"> The `fields` stores a set of unique keys each having an associated value parameter. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: c95dbdc9c72cc8e7f2367d7ef2c6d99c5e013233" \ -d '{ "name":"UserCustomProfile", "fields":{}, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="3. Set fields of the custom class"> Set as many fields for a new class as you need. ```curl {6-11} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: c95dbdc9c72cc8e7f2367d7ef2c6d99c5e013233" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="4. Use a `permissions` parameter to set operations allowed and access levels"> ```curl {12} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: c95dbdc9c72cc8e7f2367d7ef2c6d99c5e013233" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, "permissions":{}, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="5. Set CRUD operations"> Set the operations that can be performed on a record. Here, the `create`, `read`, and `update` operations are set. ```curl {13-15} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: c95dbdc9c72cc8e7f2367d7ef2c6d99c5e013233" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, "permissions":{ "create":{}, "read":{}, "update":{}, }, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="6. Set access levels"> Set an access level for a record. Here, the `open`, `not_allowed`, and `open_for_users_ids` access levels are set. ```curl {14,17,20} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: c95dbdc9c72cc8e7f2367d7ef2c6d99c5e013233" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, "permissions":{ "create":{ "access":"open" }, "read":{ "access":"not_allowed" }, "update":{ "access":"open_for_users_ids" }, }, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="7. Set IDs of users"> Set IDs of users who can make a required operation. ```curl {21} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: c95dbdc9c72cc8e7f2367d7ef2c6d99c5e013233" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, "permissions":{ "create":{ "access":"open" }, "read":{ "access":"not_allowed" }, "update":{ "access":"open_for_users_ids", "ids":"1,2" }, }, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="8. As a result, the API returns a newly created class."> ```json theme={null} { "_id": "5ddfb849a0eb4725dfc9b0ad", "acl": { "read": { "access": "not_allowed" }, "update": { "access": "open_for_users_ids", "users_ids": [ "1", "2" ] }, "delete": { "access": "owner" }, "create": { "access": "open", "primary": true } }, "application_id": 78387, "custom_fields": [ { "name": "name", "type": "String" }, { "name": "last_name", "type": "String" }, { "name": "age", "type": "Integer" }, { "is_array": "1", "name": "tags", "type": "String" } ], "name": "UserCustomProfile", "user_id": 91103344 } ``` </Step> </Steps> </Accordion> <Accordion title="Create class with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name":"UserCustomProfile", }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="2. Set a name of the classs"> Use a `name` parameter to set the name of the class. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name":"UserCustomProfile", }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="3. Use a `fields` parameter to set custom fields"> The `fields` stores a set of unique keys each having an associated value parameter. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name":"UserCustomProfile", "fields":{}, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="4. Set fields of the custom class"> Set as many fields for a new class as you need. ```curl {6-11} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="5. Use a `permissions` parameter to set operations allowed and access levels"> ```curl {12} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, "permissions":{}, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="6. Set CRUD operations"> Set the operations that can be performed on a record. Here, the `create`, `read`, and `update` operations are set. ```curl {13-15} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, "permissions":{ "create":{}, "read":{}, "update":{}, }, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="7. Set access levels"> Set an access level for a record. Here, the `open`, `not_allowed`, and `open_for_users_ids` access levels are set. ```curl {14,17,20} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, "permissions":{ "create":{ "access":"open" }, "read":{ "access":"not_allowed" }, "update":{ "access":"open_for_users_ids" }, }, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="8. Set IDs of users"> Set IDs of users who can make a required operation. ```curl {21} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, "permissions":{ "create":{ "access":"open" }, "read":{ "access":"not_allowed" }, "update":{ "access":"open_for_users_ids", "ids":"1,2" }, }, }' \ "https://api.quickblox.com/class.json" ``` </Step> <Step title="9. As a result, the API returns a newly created class."> ```json theme={null} { "_id": "5ddfb849a0eb4725dfc9b0ad", "acl": { "read": { "access": "not_allowed" }, "update": { "access": "open_for_users_ids", "users_ids": [ "1", "2" ] }, "delete": { "access": "owner" }, "create": { "access": "open", "primary": true } }, "application_id": 78387, "custom_fields": [ { "name": "name", "type": "String" }, { "name": "last_name", "type": "String" }, { "name": "age", "type": "Integer" }, { "is_array": "1", "name": "tags", "type": "String" } ], "name": "UserCustomProfile", "user_id": 91103344 } ``` </Step> </Steps> </Accordion> </AccordionGroup> **Allowed permissions** | Permission | Syntax | Example | | ------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Open | permissions.\<CRUD\_operation>.access | permissions.read.access=open | | Owner | permissions.\<CRUD\_operation>.access | permissions.read.access=owner | | Not allowed | permissions.\<CRUD\_operation>.access | permissions.read.access=not\_allowed | | Open for users IDs | permissions.\<CRUD\_operation>.access=open\_for\_users\_ids permissions.\<CRUD\_operation>.ids=id\_1,id\_2,id\_3,… | permissions.update.access=open\_for\_users\_ids permissions.update.ids=3,12 | | Open for groups | permissions.\<CRUD\_operation>.access=open\_for\_groups permissions.\<CRUD\_operation>.groups=group\_name\_1,group\_name\_2 | permissions.delete.access=open\_for\_groups permissions.delete.groups=experience,rate | #### Body Parameters <ParamField type="string"> Custom class name. </ParamField> <ParamField type="object"> Custom class fields with types. Format: `fields.{custom_field}=<field_type>`. <Expandable title="fields object"> <ParamField type="string"> Name and type of a custom field. Can be many 1..N. Possible plain types: `Integer`, `Float`, `String`, `Boolean`, `File`, `Date`, `Location` (array of \[longitude, latitude]). Possible array types: `Integer_a`, `Float_a`, `Boolean_a`, `String_a`. </ParamField> </Expandable> </ParamField> <ParamField type="string"> Class permissions. Format: <br /> `permission.<CRUD_operation>.access=<value>` <br /> `permission.<CRUD_operation>.<option>=<value>` CRUD operations: `create`, `read`, `update`, `delete`. <br /> CRUD access values: `open`, `owner`, `not_allowed`, `open_for_users_ids`, `open_for_groups`. <br /> CRUD options: `ids`, `groups`. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="_id" type="string" /> <ResponseField name="acl" type="object"> <Expandable title="properties"> <ResponseField name="read" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string"> Read access permission </ResponseField> </Expandable> </ResponseField> <ResponseField name="update" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string"> Update access permission </ResponseField> <ResponseField name="users_ids" type="array of strings"> Array of user IDs </ResponseField> </Expandable> </ResponseField> <ResponseField name="delete" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string"> Delete access permission </ResponseField> </Expandable> </ResponseField> <ResponseField name="create" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string"> Create access permission </ResponseField> <ResponseField name="primary" type="boolean" /> </Expandable> </ResponseField> </Expandable> </ResponseField> <ResponseField name="application_id" type="integer"> The ID of the associated application </ResponseField> <ResponseField name="custom_fields" type="array"> <Expandable title="properties"> <ResponseField name="is_array" type="boolean" /> <ResponseField name="name" type="string"> Name of the custom field </ResponseField> <ResponseField name="type" type="string"> Type of the custom field </ResponseField> </Expandable> </ResponseField> <ResponseField name="name" type="string"> Name of the resource </ResponseField> <ResponseField name="user_id" type="integer"> The ID of the associated user </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="422"> An error response <Expandable title="Option 1"> <ResponseField name="custom_fields" type="array of strings" /> </Expandable> <Expandable title="Option 2"> <ResponseField name="name" type="array of strings" /> </Expandable> <Expandable title="Option 3"> <ResponseField name="acl" type="array of strings" /> </Expandable> <Expandable title="Option 4"> <ResponseField name="base" type="array of strings" /> </Expandable> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: c95dbdc9c72cc8e7f2367d7ef2c6d99c5e013233" \ -d '{ "name":"UserCustomProfile", "fields":{ "name":"String", "last_name":"String", "age":"Integer", "tags":"String_a" }, "permissions":{ "create":{ "access":"open" }, "read":{ "access":"not_allowed" }, "update":{ "access":"open_for_users_ids", "ids":"1,2" } } }' \ https://api.quickblox.com/class.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "_id": "5ddfb849a0eb4725dfc9b0ad", "acl": { "read": { "access": "not_allowed" }, "update": { "access": "open_for_users_ids", "users_ids": [ "1", "2" ] }, "delete": { "access": "owner" }, "create": { "access": "open", "primary": true } }, "application_id": 78387, "custom_fields": [ { "name": "name", "type": "String" }, { "name": "last_name", "type": "String" }, { "name": "age", "type": "Integer" }, { "is_array": "1", "name": "tags", "type": "String" } ], "name": "UserCustomProfile", "user_id": 91103344 } ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to do this operation" ] } ``` ```json 422 theme={null} { "name": [ "can't be blank", "must contain only alphanumeric or underscore characters and begin with a letter.", "is too short (minimum is 3 characters)", "is too long (maximum is 50 characters)" ] } ``` </ResponseExample> # Create Dialog Source: https://docs.quickblox.com/reference/create-dialog POST https://api.quickblox.com/chat/Dialog.json Create a public/group/private dialog. #### Recipes <AccordionGroup> <Accordion title="Create private dialog"> <Steps> <Step title="1. Set a private dialog type"> Use a `type` parameter to set a dialog type. Set it to `3` to create a private dialog. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":3, "occupants_ids":"102433721" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Set the ID of the opponent"> Use an `occupants_ids` parameter to set the ID of the opponent who is going to participate in the dialog. ```{6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":3, "occupants_ids":"102433721" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. As a result, the API returns a newly created private dialog."> ``` curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":3, "occupants_ids":"102433721" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create public dialog"> <Steps> <Step title="1. Set a public dialog type"> Use a `type` parameter to set a dialog type. Set it to `1` to create a public dialog. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Set a name of the public dialog"> Use a `name` parameter to set a name for the group dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set an avatar for the dialog"> You can set an avatar for a public dialog. Just set the ID of the file in the `photo` field. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. As a result, the API returns a newly created public dialog."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create group dialog"> <Steps> <Step title="1. Set a group dialog type"> Set a `type` parameter to `2` to create a group dialog. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Set a name of the group dialog"> Use a `name` parameter to set a name for the group dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set IDs of opponents"> Use an `occupants_ids` parameter to set a list of opponents IDs who are going to participate in the dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. As a result, the API returns a newly created group dialog."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create dialog with custom parameters"> <Steps> <Step title="1. Set a type of the dialog"> You can set a type of dialog using a `type` parameter. Here, a `group` dialog is created since the `type` is set to `2`. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "data":{ "class_name":"ScoreTable", "game_mode_name":"rainbow dash", "expert_mode":"3", "progress":"56", } }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Set a name of the dialog"> Use a name parameter to set a name for the group dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "data":{ "class_name":"ScoreTable", "game_mode_name":"rainbow dash", "expert_mode":"3", "progress":"56", } }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set IDs of opponents"> Use an `occupants_ids` parameter to set a list of opponents IDs who are going to participate in the dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "data":{ "class_name":"ScoreTable", "game_mode_name":"rainbow dash", "expert_mode":"3", "progress":"56", } }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. Use a `data` parameter to refer to a custom object that should be added"> You need to refer to the specific custom object in the request. See [this section](/reference/create-class) to learn how to create a custom object class. The `data` stores a set of unique keys each having an associated value parameter. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "data":{ "class_name":"ScoreTable", "game_mode_name":"rainbow dash", "expert_mode":"3", "progress":"56", } }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. Set a name of the class"> Here, we refer to the `ScoreTable` custom object class. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "data":{ "class_name":"ScoreTable", "game_mode_name":"rainbow dash", "expert_mode":"3", "progress":"56", } }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="6. Set custom object fields"> Here, we refer to custom object fields of the `ScoreTable` class. ```curl {10-12} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "data":{ "class_name":"ScoreTable", "game_mode_name":"rainbow dash", "expert_mode":"3", "progress":"56", } }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="7. As a result, the API returns a newly created dialog with the specified custom parameters."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "data":{ "class_name":"ScoreTable", "game_mode_name":"rainbow dash", "expert_mode":"3", "progress":"56", } }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create group dialog with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set a group dialog type"> Set a `type` parameter to `2` to create a group dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. Set a name of the group dialog"> Use a name parameter to set a name for the group dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. Set IDs of opponents"> Use an `occupants_ids` parameter to set a list of opponents IDs who are going to participate in the dialog. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="6. As a result, the API returns a newly created group dialog."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create private dialog with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":3, "occupants_ids":"102433721" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":3, "occupants_ids":"102433721" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set a private dialog type"> Use a `type` parameter to set a dialog type. Set it to `3` to create a private dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":3, "occupants_ids":"102433721" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. Set the ID of the opponent"> Use an `occupants_ids` parameter to set the ID of the opponent who is going to participate in the dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":3, "occupants_ids":"102433721" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. As a result, the API returns a newly created private dialog."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":3, "occupants_ids":"102433721" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create public dialog with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set a public dialog type"> Use a `type` parameter to set a dialog type. Set it to `1` to create a public dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. Set a name of the public dialog"> Use a name parameter to set a name for the group dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. Set an avatar for the dialog"> You can set an avatar for a public dialog. Just set the ID of the file in the photo field. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="6. As a result, the API returns a newly created public dialog."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create group dialog with is_join_required parameter (added in v2.34.0)"> <Steps> <Step title="1. Set dialog type"> Use the `type` parameter to define the dialog type. `type=2` creates a GROUP dialog. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "is_join_required":0 }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Set dialog name"> Use the `name` parameter to define the name of the group dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "is_join_required":0 }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set occupants IDs"> Use the `occupants_ids` parameter to define users who will participate in the dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "is_join_required":0 }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. Disable join requirement"> Set `"is_join_required": 0` to allow users to send and receive messages via XMPP without performing a join operation. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "is_join_required":0 }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. Result"> The API returns a newly created group dialog with `is_join_required` set to `0`. ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "type":2, "name":"Chat with Bob, Sam, Garry", "occupants_ids":"96754523,96830631,96831328", "is_join_required": 0 }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create group dialog with admin IDs (added in v2.52.0)"> <Steps> <Step title="1. Set a group dialog type"> Set a `type` parameter to `2` to create a group dialog. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Set a name of the group dialog"> Use a `name` parameter to set a name for the group dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set IDs of opponents"> Use an `occupants_ids` parameter to set a list of opponents IDs who are going to participate in the dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. Set IDs of admins"> Use the `admin_ids` parameter to set a list of admin IDs who will moderate the dialog. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. As a result, the API returns a newly created group dialog."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create public dialog with admin IDs (added in v2.52.0)"> <Steps> <Step title="1. Set a public dialog type"> Use a `type` parameter to set a dialog type. Set it to `1` to create a public dialog. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Set a name of the public dialog"> Use a `name` parameter to set a name for the group dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set an avatar for the dialog"> You can set an avatar for a public dialog. Just set the ID of the file in the `photo` field. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. Set IDs of admins"> Use the `admin_ids` parameter to set a list of admin IDs who will moderate the dialog. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. As a result, the API returns a newly created public dialog."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create group dialog with admin IDs using API key (added in v2.52.0)"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set a group dialog type"> Set a `type` parameter to `2` to create a group dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. Set a name of the group dialog"> Use a name parameter to set a name for the group dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. Set IDs of opponents"> Use an `occupants_ids` parameter to set a list of opponents IDs who are going to participate in the dialog. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="6. Set IDs of admins"> Use the `admin_ids` parameter to set a list of admin IDs who will moderate the dialog. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="7. As a result, the API returns a newly created group dialog."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":2, "name":"New Year party", "occupants_ids":"96831397,96831328,96830631,96754523", "photo":"68123" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Create public dialog with admin IDs using API key (added in v2.52.0)"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set a public dialog type"> Use a `type` parameter to set a dialog type. Set it to `1` to create a public dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. Set IDs of admins"> Use the `admin_ids` parameter to set a list of admin IDs who will moderate the dialog. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834", "admin_ids":"96831397,96831328" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. Set a name of the public dialog"> Use a name parameter to set a name for the group dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="5. Set an avatar for the dialog"> You can set an avatar for a public dialog. Just set the ID of the file in the photo field. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="6. As a result, the API returns a newly created public dialog."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "type":1, "name":"Soccer news", "photo":"67834" }' \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> </AccordionGroup> Use `type=1` (PUBLIC\_GROUP) to create a public dialog. All users from your application will be able to join it. The server will create a public chat and return a detailed information about the newly created dialog. Field `xmpp_room_jid` will contain a Chat room JID to which you should connect to start chatting. Use `type=2` (GROUP) to create a group dialog only for specific users provided in `occupants_ids`. The server will create a group chat (involving only members of this chat) and return a detailed information about the newly created dialog. Field `xmpp_room_jids` will contain a Chat room JID to which you should connect to start chatting. Use `type=3` (PRIVATE) to create a private dialog between 2 users. The server will return a detailed information about the newly created dialog. If a user sends a chat message to some user and the private dialog wasn't created, it will be created automatically with the first chat message. **Custom parameters** Dialogs can store additional parameters. These parameters can be used to store additional data. Also, these parameters can be used in dialogs retrieval requests. To start using additional parameters, create an additional schema of your parameters. This is a custom objects class. Just create an empty class with all fields that you need. These fields will be your dialog's additional parameters. Next, to set additional parameters to a dialog, use next additional parameters in a creation request: `data[class_name]` - should contain **Custom Objects class** name created above.\ `data[...]`\ `data[{field_name_N}]` - field N from Custom Object class. **Admin role overview** (added in v2.52.0) * Admins are like occupants, but with elevated permissions. * Only supported in **public** and **group** dialogs (`type: 1`, `type: 2`). * Users in `admin_ids` must also be in `occupants_ids`(group dialog). If not — they will be automatically added. * Ignored in **private** dialogs (`type: 3`). *Permissions:* * Rename the dialog * Change the photo * Edit and delete **any** messages * Remove other occupants (except the owner) * Remove themselves from `admin_ids` #### Body Parameters <ParamField type="integer"> Type of a new dialog. Possible values are: <br /> `1` - PUBLIC\_GROUP <br /> `2` - GROUP <br /> `3` - PRIVATE <br /> </ParamField> <ParamField type="string"> Name of a new dialog.Ignored when `type=3` (PRIVATE).The maximum length is 200 symbols.</ParamField> <ParamField type="string"> Photo of a new dialog. </ParamField> <ParamField type="string"> IDs of dialog occupants (users who will be able to chat in this dialog). Don't need to pass current user ID, it will be added automatically. Can be a String or Array of Integers. </ParamField> <ParamField type="string"> IDs of users with admin permissions in the dialog. Ignored when `type=3` (PRIVATE). Admins can update the name and photo, edit or delete any messages, and manage occupants. (added in v2.52.0) </ParamField> <ParamField type="integer"> Defines whether a user must perform a join operation before sending and receiving messages via XMPP. Applies only to `type=2` (GROUP). Possible values: <br /> `0` - join not required, <br /> `1` - join required. <br /> Default for GROUP dialogs is `0` when the field is not provided. The default value can be configured in the Dashboard → Settings. <br /> Ignored for `type=1` (PUBLIC\_GROUP) and `type=3` (PRIVATE) — if provided in the request, the value is not stored (added in v2.34.0). </ParamField> <ParamField type="object"> <Expandable title="propetries"> <ParamField type="string"> Class name in Custom Objects. </ParamField> <ParamField type="string"> Field name of class in Custom Objects. Can be many 1..N. </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See our [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="_id" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="last_message" type="string" /> <ResponseField name="last_message_date_sent" type="integer" /> <ResponseField name="last_message_id" type="string" /> <ResponseField name="last_message_user_id" type="integer" /> <ResponseField name="name" type="string" /> <ResponseField name="occupants_ids" type="array of integers" /> <ResponseField name="admin_ids" type="array of integers (added in v2.52.0)" /> <ResponseField name="photo" type="string" /> <ResponseField name="type" type="integer" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="xmpp_room_jid" type="string" /> <ResponseField name="unread_messages_count" type="string" /> <ResponseField name="is_join_required" type="integer" /> </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="422"> An error response <ResponseField name="type" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "type":3, "occupants_ids":"102433721" }' \ https://api.quickblox.com/chat/Dialog.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "_id": "5e261c72a28f9a4f779f10bb", "created_at": "2020-01-20T21:32:34Z", "last_message": "Hello world", "last_message_date_sent": 1597748932, "last_message_id": "5f3bb6c4a28f9a69552944d1", "last_message_user_id": 102433734, "name": "Bob", "occupants_ids": [ 102433721, 102433734 ], "admin_ids": [], "photo": null, "type": 3, "updated_at": "2020-08-18T11:08:52Z", "user_id": 102433721, "xmpp_room_jid": null, "unread_messages_count": null, "is_join_required": null } ``` ```json 400 theme={null} // Occupants_ids doesn't contain opponent ID { "errors": ["Occupants_ids is missing opponent ID."] } // Invalid admin_ids format (must be an array of int) { "errors": ["Incorrect format for parameter \"admin_ids\"."] } ``` ```json 404 theme={null} { "errors": ["Users with these IDs for \"admin_ids\" do not exist."] } ``` ```json 422 theme={null} { "type": ["is wrong."] } ``` </ResponseExample> # Create Event Source: https://docs.quickblox.com/reference/create-event POST https://api.quickblox.com/events.json Create notification event. #### Recipe <AccordionGroup> <Accordion title="Create event"> <Steps> <Step title="1. Use an `event` parameter to set data associated with the event"> ```curl {5-6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="2. Set a notification type"> Use a `notication_type` parameter to set the type of notification. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="3. Set a push notification environment"> Use an `environment` parameter to set the notification environment. Here, the production environment is set which means the pushes will work for a production environment only. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="4. Use a `user` parameter to set the ID of push notification recipient."> ```curl {8-9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ }, } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="5. Set the ID of push notification recipient"> You can specify as many IDs as you need via comma. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ "ids":"96831397" }, } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="6. Set a push message"> A push message text should be Base64-encoded. Here, the iOS push message is set. For iOS push, you should add a payload before a message. ```curl {11} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ "ids":"96831397" }, "message":"payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="7. Set a push notification platform"> Use a `push_type` parameter to set the push notification platform. Here, the `push_type` is set to `apns`. This means that the push will be delivered to iOS devices. ```curl {12} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ "ids":"96831397" }, "message":"payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", "push_type":"apns" } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="8. As a result, the API returns a newly created event."> ```json theme={null} { "event": { "id": 30961913, "event_type": "one_shot", "message": "payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", "date": null, "period": null, "name": null, "occured_count": 0, "created_at": "2019-11-09T19:40:25Z", "updated_at": "2019-11-09T19:40:25Z", "end_date": null, "active": true, "application_id": 76730, "user_id": 91142495, "kind": "API", "environment": "production", "tag_query": null, "notification_channel": { "name": "apns" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Create event with period date"> <Steps> <Step title="1. Use an `event` parameter to set data associated with the event"> ```curl {5-6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="2. Set a notification type"> Use a `notication_type` parameter to set the type of notification. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="3. Set a push notification environment"> Use an `environment` parameter to set the notification environment. Here, the production environment is set which means the pushes will work for a production environment only. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="4. Set an event type"> Use an `event_type` method to set the event type. Here, the `period_date` event type is set. This event occurs within a given period from the initial date. Valid only if the `period` is specified. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "event_type":"period_date", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="5. Set a date of the event"> Use a `date` parameter to set the date for the event to be sent on. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "event_type":"period_date", "date":"1567556125", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="6. Set a period of the event"> Use a `period` parameter to set the period of the event. ```curl {10} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "event_type":"period_date", "date":"1567556125", "period":"86400", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="7. Set an end date of the event"> Use an `end_date` parameter to set the date for the event to end. ```curl {11} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "event_type":"period_date", "date":"1567556125", "period":"86400", "end_date":"1568556125", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="8. Set a push message"> A push message text should be Base64-encoded. Here, the iOS push message is set. For iOS push, you should add a payload before a message. ```curl {12} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "event_type":"period_date", "date":"1567556125", "period":"86400", "end_date":"1568556125", "message":"payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="9. Set a push notification platform"> Use a `push_type` parameter to set the push notification platform. Here, the `push_type` is set to `apns`. This means that the push will be delivered to iOS devices. ```curl {13} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1c3c3584d472a4416eca65df9474ed2cee012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "event_type":"period_date", "date":"1567556125", "period":"86400", "end_date":"1568556125", "message":"payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", "push_type":"apns" } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="10. As a result, the API returns a newly created event."> ```json theme={null} { "event": { "id": 30534811, "event_type": "period_date", "message": "payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", "date": 1567556125, "period": 86400, "name": null, "occured_count": 1, "created_at": "2019-09-02T20:29:55Z", "updated_at": "2019-09-02T20:29:55Z", "end_date": 1568556125, "active": true, "application_id": 76730, "user_id": 91141808, "kind": "API", "environment": "production", "tag_query": null, "notification_channel": { "name": "apns" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Create event for users with tags"> <Steps> <Step title="1. Use an `event` parameter to set data associated with the event"> ```curl {5-6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="2. Set a notification type"> Use a `notication_type` parameter to set the type of notification. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="3. Set a push notification environment"> Use an `environment` parameter to set the notification environment. Here, the production environment is set which means the pushes will work for a production environment only. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", } }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="4. Use a `user` parameter to set user tags."> ```curl {8-9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ } }, }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="5. Use a `tags` parameter to set user tags"> ```curl {9-10} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ "tags":{ } } }, }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="6. Set user tags"> Specify as many tags as you need via comma. ```curl {10} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ "tags":{ "any":"web,android" } } }, }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="7. Set a push message"> A push message text should be Base64-encoded. Here, the iOS push message is set. For iOS push, you should add a payload before a message. ```curl {13} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ "tags":{ "any":"web,android" } }, "message":"payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", }, }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="8. Set a push notification platform"> Use a `push_type` parameter to set the push notification platform. Here, the `push_type` is set to `apns`. This means that the push will be delivered to iOS devices. ```curl {14} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ "tags":{ "any":"web,android" } }, "message":"payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", "push_type":"apns" }, }' \ https://api.quickblox.com/events.json ``` </Step> <Step title="9. As a result, the API returns a newly created event for users with the specified tags."> ```json theme={null} { "event": { "id": 30962142, "event_type": "one_shot", "message": "payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", "date": null, "period": null, "name": null, "occured_count": 0, "created_at": "2019-11-09T20:40:25Z", "updated_at": "2019-11-09T20:40:25Z", "end_date": null, "active": true, "application_id": 76730, "user_id": 91142495, "kind": "API", "environment": "production", "tag_query": "{\"any\":\"web,android\"}", "notification_channel": { "name": "apns" } } } ``` </Step> </Steps> </Accordion> </AccordionGroup> <Warning> Only one of **(Optional)** parameters is required. </Warning> #### Body Parameters <ParamField type="string"> Type of notification. Possible values are: `push` and `email.` </ParamField> <ParamField type="string"> Used only if `notification_type=push`, otherwise ignored. If not set, the notification will be delivered to all possible devices for specified users. Possible values are: `apns`, `apns_voip`, `gcm`. </ParamField> <ParamField type="string"> An environment of the notification. Possible values are: `development` and `production`. </ParamField> <ParamField type="string"> Event type. Possible values are: * `one_shot` - a one-time event caused by an external object (valid only if the `date` is **not** specified). * `fixed_date` - a one-time event that occurs at a specified date (valid only if the `date` is specified). * `period_date` - is a reusable event that occurs within a given period from the initial date (valid only if the `period` is specified).\ Default: * `one_shot` - if `date` is **not** specified. * `period_date` - if `period` is specified. * `fixed_date` - if `date` is specified. </ParamField> <ParamField type="string"> Encoded message payload. Possible values are: Base64 encoded plain text or push notification payload ([Push notifications formats](#push-notification-formats)). </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> **(Optional)** Notification's recipients should contain a string of users' IDs divided by commas. </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> **(Optional)** Notification recipients should contain a string of tags divided by commas. Recipients must have at least one tag specified in the list. </ParamField> <ParamField type="string"> **(Optional)** Notification recipients should contain a string of tags divided by commas. Recipients must have ALL tags specified in the list. </ParamField> <ParamField type="string"> **(Optional)** Notification recipients should contain a string of tags divided by commas. Recipients must not have tags specified in the list. </ParamField> </Expandable> </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="external_user object"> <ParamField type="string"> **(Optional)** Notification recipients should contain a string of external users' IDs divided by commas. </ParamField> </Expandable> </ParamField> <ParamField type="date-time"> The date of the event to send on. If the `event_type=fixed_date`, the `date` can not be in the past. </ParamField> <ParamField type="date-time"> The date when the event is completed. Must be not less than the `date`. </ParamField> <ParamField type="date-time"> The period of the event in seconds. </ParamField> <ParamField type="string"> The name of the event. Service information. Only for your own usage. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response. <ResponseField name="event" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="event_type" type="string" /> <ResponseField name="message" type="string" /> <ResponseField name="date" type="string" /> <ResponseField name="period" type="string" /> <ResponseField name="name" type="string" /> <ResponseField name="occured_count" type="integer" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="end_date" type="string" /> <ResponseField name="active" type="boolean" /> <ResponseField name="application_id" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="kind" type="string" /> <ResponseField name="environment" type="string" /> <ResponseField name="tag_query" type="string" /> <ResponseField name="notification_channel" type="object"> <Expandable title="properties"> <ResponseField name="name" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 519c40c31072fe5dcbe0c24651c7489fab012bba" \ -d '{ "event":{ "notification_type":"push", "environment":"production", "user":{ "ids":"96831397" }, "message":"payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", "push_type":"apns" } }' \ https://api.quickblox.com/events.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "event": { "id": 30961913, "event_type": "one_shot", "message": "payload=eyJhcHMiOnsiYWxlcnQiOiJIZWxsbywgV29ybGQhIiwiYmFkZ2UiOjc3Nywic291bmQiOiJkZWZhdWx0In19", "date": null, "period": null, "name": null, "occured_count": 0, "created_at": "2019-11-09T19:40:25Z", "updated_at": "2019-11-09T19:40:25Z", "end_date": null, "active": true, "application_id": 76730, "user_id": 91142495, "kind": "API", "environment": "production", "tag_query": null, "notification_channel": { "name": "apns" } } } ``` </ResponseExample> # Create File Source: https://docs.quickblox.com/reference/create-file POST https://api.quickblox.com/blobs.json Create an entity (file/blob) that is a file in the system. #### Recipe <AccordionGroup> <Accordion title="Create a public file"> <Steps> <Step title="1. Use a `blob` parameter to set blob fields"> ```curl {5-6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ } }' \ https://api.quickblox.com/blobs.json ``` </Step> <Step title="2. Set a content type of the file"> Use a `content-type` parameter to set a content type of the file. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ "content_type":"image/jpeg", } }' \ https://api.quickblox.com/blobs.json ``` </Step> <Step title="3. Set a name of the file"> Use a `name` parameter to set the name of the file. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ "content_type":"image/jpeg", "name":"my_cat.jpeg", } }' \ https://api.quickblox.com/blobs.json ``` </Step> <Step title="4. Set a file as public"> Set a `public` parameter as true to make a file public. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ "content_type":"image/jpeg", "name":"my_cat.jpeg", "public":true, } }' \ https://api.quickblox.com/blobs.json ``` </Step> <Step title="5. Set tags to a file"> Use a `tag_list` to assign tags to a file. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ "content_type":"image/jpeg", "name":"my_cat.jpeg", "public":true, "tag_list":"web,android" } }' \ https://api.quickblox.com/blobs.json ``` </Step> <Step title="6. As a result, the API returns a newly created public file."> ```json theme={null} { "blob": { "id": 10070416, "uid": "20456a9676674f4ca950d963dfca630e00", "content_type": "image/jpeg", "name": "my_cat.jpeg", "size": null, "created_at": "2020-07-29T11:06:31Z", "updated_at": "2020-07-29T11:06:31Z", "blob_status": null, "set_completed_at": null, "public": true, "account_id": 105726, "app_id": 78162, "blob_object_access": { "id": 10070416, "blob_id": 10070416, "expires": "2020-07-29T12:06:31Z", "object_access_type": "Write", "params": "https://api.quickblox.com/blobs/10070416/upload/?acl=authenticated-read&content_type=image%2Fjpeg&expires=2020-07-29%2012%3A06%3A31%20UTC&key=20456a9676674f4ca950d963dfca630e00&policy=OTVhNDNiZDNiMWMxMGZlNDgzMmE1MzEyYzE4MjYyZjI%3D%0A&success_action_status=201&x-amz-algorithm=AWS4-HMAC-SHA256&x-amz-credential=d9edd8e29a9f029549ecf6313c67c676%2F20200729%2Fqbprod%2Fs3%2Faws4_request&x-amz-date=2020211T1206Z&x-amz-signature=84901614d08e3d2d1fbba76dc5378242" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Create a private file"> <Steps> <Step title="1. Use a `blob` parameter to set blob fields"> ```curl {5-6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ } }' \ https://api.quickblox.com/blobs.json ``` </Step> <Step title="2. Set a content type of the file"> Use a `content-type` parameter to set a content type of the file. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ "content_type":"image/jpeg", } }' \ https://api.quickblox.com/blobs.json ``` </Step> <Step title="3. Set a name of the file"> Use a `name` parameter to set the name of the file. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ "content_type":"image/jpeg", "name":"museum.jpeg" } }' \ https://api.quickblox.com/blobs.json ``` </Step> <Step title="4. As a result, the API returns a newly created private file."> ```json theme={null} { "blob": { "id": 10070416, "uid": "20456a9676674f4ca950d963dfca630e00", "content_type": "image/jpeg", "name": "museum.jpeg", "size": null, "created_at": "2020-07-29T11:06:31Z", "updated_at": "2020-07-29T11:06:31Z", "blob_status": null, "set_completed_at": null, "public": false, "account_id": 105726, "app_id": 78162, "blob_object_access": { "id": 10070416, "blob_id": 10070416, "expires": "2020-07-29T12:06:31Z", "object_access_type": "Write", "params": "https://api.quickblox.com/blobs/10070416/upload/?acl=authenticated-read&content_type=image%2Fjpeg&expires=2020-07-29%2012%3A06%3A31%20UTC&key=20456a9676674f4ca950d963dfca630e00&policy=OTVhNDNiZDNiMWMxMGZlNDgzMmE1MzEyYzE4MjYyZjI%3D%0A&success_action_status=201&x-amz-algorithm=AWS4-HMAC-SHA256&x-amz-credential=d9edd8e29a9f029549ecf6313c67c676%2F20200729%2Fqbprod%2Fs3%2Faws4_request&x-amz-date=2020211T1206Z&x-amz-signature=84901614d08e3d2d1fbba76dc5378242" } } } ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Body Parameters <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> <Tooltip> MIME content type</Tooltip> </ParamField> <ParamField type="string">File/blob name</ParamField> <ParamField type="string"> File/blob visibility. If the file is public, it's possible to download it without a session token. </ParamField> <ParamField type="string">Should be a comma-separated string with tags.</ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="blob" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="uid" type="string" /> <ResponseField name="content_type" type="string" /> <ResponseField name="name" type="string" /> <ResponseField name="size" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="blob_status" type="string" /> <ResponseField name="set_completed_at" type="string" /> <ResponseField name="public" type="boolean" /> <ResponseField name="account_id" type="integer" /> <ResponseField name="app_id" type="integer" /> <ResponseField name="blob_object_access" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="blob_id" type="integer" /> <ResponseField name="expires" type="string" /> <ResponseField name="object_access_type" type="string" /> <ResponseField name="params" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="name" type="array of strings" /> <ResponseField name="content_type" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "blob":{ "content_type":"image/jpeg", "name":"museum.jpeg" } }' \ https://api.quickblox.com/blobs.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "blob": { "id": 10070416, "uid": "20456a9676674f4ca950d963dfca630e00", "content_type": "image/jpeg", "name": "test2.jpg", "size": null, "created_at": "2020-07-29T11:06:31Z", "updated_at": "2020-07-29T11:06:31Z", "blob_status": null, "set_completed_at": null, "public": false, "account_id": 105726, "app_id": 78162, "blob_object_access": { "id": 10070416, "blob_id": 10070416, "expires": "2020-07-29T12:06:31Z", "object_access_type": "Write", "params": "https://api.quickblox.com/blobs/10070416/upload/?acl=authenticated-read&content_type=image%2Fjpeg&expires=2020-07-29%2012%3A06%3A31%20UTC&key=20456a9676674f4ca950d963dfca630e00&policy=OTVhNDNiZDNiMWMxMGZlNDgzMmE1MzEyYzE4MjYyZjI%3D%0A&success_action_status=201&x-amz-algorithm=AWS4-HMAC-SHA256&x-amz-credential=d9edd8e29a9f029549ecf6313c67c676%2F20200729%2Fqbprod%2Fs3%2Faws4_request&x-amz-date=2020211T1206Z&x-amz-signature=84901614d08e3d2d1fbba76dc5378242" } } } ``` ```json 422 theme={null} { "errors": { "name": [ "can't be blank", "is too short (minimum is 1 character)", "is too long (maximum is 100 characters)" ], "content_type": [ "is invalid", "is too short (minimum is 5 characters)", "is too long (maximum is 100 characters)" ] } } ``` </ResponseExample> # Create Message Source: https://docs.quickblox.com/reference/create-message POST https://api.quickblox.com/chat/Message.json Create a chat message. #### Recipes <AccordionGroup> <Accordion title="Create message with extra data"> <Steps> <Step title="1. Set the ID of the dialog"> Set a `chat_dialog_id` to send a message to the specific dialog. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="2. Type a message"> Enter a message text using the message parameter. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="3. Specify if you want to send a message"> Set a `send_to_chat` parameter to `1` to send a message to the dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="4. Add extra data to a message"> You can extend a message with additional fields by setting key-value custom parameters. Here, the `is_pinned` is a custom parameter key and the `false` is a custom parameter value. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="5. As a result, the API returns a message with additional fields."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> </Steps> </Accordion> <Accordion title="Create message in group dialog"> <Steps> <Step title="1. Set ID of the dialog"> Use a `chat_dialog_id` parameter to create a message for the specific dialog. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="2. Type a message"> Enter a `message` text using the message parameter. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="3. Specify if you want to send a message"> Set a `send_to_chat` parameter to `1` to send a message to the dialog. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="4. Specify if you want to mark messages as read/delivered"> Set a `markable` parameter to `1` to mark all messages as read/delivered. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="5. Set message attachment"> You can add an attachement to the message using the `attachment` parameter. Just specify the `ID` of the file on QuickBlox server and its `type`. You can add as many attachments as you need. ```curl {9-18} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="6. As a result, the API returns a newly created message"> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> </Steps> </Accordion> <Accordion title="Create message in private dialog"> <Steps> <Step title="1. Set the ID of the message recipient"> Use a `recipient_id` parameter to set the ID of the message recipient. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: ee4b9e6cad16378aab011c236b186aec09012bba" \ -d '{ "recipient_id":102460596, "message":"Hello!" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="2. Type a message"> Enter a message text using the `message` parameter. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: ee4b9e6cad16378aab011c236b186aec09012bba" \ -d '{ "recipient_id":102460596, "message":"Hello!" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="3. As a result, the API returns a newly created message."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: ee4b9e6cad16378aab011c236b186aec09012bba" \ -d '{ "recipient_id":102460596, "message":"Hello!" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> </Steps> </Accordion> <Accordion title="Create message in private dialog with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "recipient_id":102460596, "message":"Hello!" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "recipient_id":102460596, "message":"Hello!" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="3. Set the ID of the message recipient"> Use a `recipient_id` parameter to set the ID of the message recipient. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "recipient_id":102460596, "message":"Hello!" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="4. Type a message"> Enter a message text using the message parameter. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "recipient_id":102460596, "message":"Hello!" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="5. As a result, the API returns a newly created message."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "recipient_id":102460596, "message":"Hello!" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> </Steps> </Accordion> <Accordion title="Create message with extra data and API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="3. Set the ID of the dialog"> Set a `chat_dialog_id` to send a message to the specific dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="4. Type a message"> Enter a message text using the message parameter. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="5. Specify if you want to send a message"> Set a `send_to_chat` parameter to 1 to send a message to the dialog. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="6. Add extra data to a message"> You can extend a message with additional fields by setting key-value custom parameters. Here, the `is_pinned` is a custom parameter key and the false is a custom parameter value. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="7. As a result, the API returns a message with additional fields."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"Allison will take it", "send_to_chat":1, "is_pinned":false }' \ https://api.quickblox.com/chat/Message.json ``` </Step> </Steps> </Accordion> <Accordion title="Create message in group dialog with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="3. Set ID of the dialog"> Use a `chat_dialog_id` parameter to create a message for the specific dialog. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="4. Type a message"> Enter a message text using the message parameter. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="5. Specify if you want to send a message"> Set a `send_to_chat` parameter to 1 to send a message to the dialog. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="6. Specify if you want to mark messages as read/delivered"> Set a `markable` parameter to `1` to mark all messages as read/delivered. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="7. Set message attachment"> You can add an attachement to the message using the `attachment` parameter. Just specify the `ID` of the file on QuickBlox server and its `type`. You can add as many attachments as you need. ```curl {10-19} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="8. As a result, the API returns a newly created message."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </Step> </Steps> </Accordion> </AccordionGroup> It’s possible to inject a new chat message into the chat history. In this case, this new message won't be delivered to the recipients by XMPP real-time transport, it will be just added to the history. If you want to send a message, pass `send_to_chat=1` parameter. #### Body Parameters <ParamField type="string"> ID of the dialog to which these messages will be added. </ParamField> <ParamField type="string"> Text of the <Tooltip>message (CGI escaped).</Tooltip> </ParamField> <ParamField type="string"> ID of the recipient. Useful only when dialog's `type=3` (PRIVATE). </ParamField> <ParamField type="object"> <Expandable title="propeties"> <ParamField type="object"> Array index 0..N. <Expandable title="properties"> <ParamField type="string"> ID of the file on QuickBlox server. </ParamField> <ParamField type="string"> Type of attachment. Example: `audio`, `video`, `image` or other. </ParamField> <ParamField type="string"> Link to a file in Internet. </ParamField> <ParamField type="string"> Name of attachment </ParamField> <ParamField type="string"> Size of attachment </ParamField> <ParamField type="string"> Content-Type of attachment </ParamField> <ParamField type="string"> Width of Image/Video. Useful for Image/Video type attachments </ParamField> <ParamField type="string"> Height of Image/Video. Useful for Image/Video type attachments </ParamField> <ParamField type="string"> Duration of Video. Useful for Video type attachments </ParamField> <ParamField type="json"> Custom parameters. Useful for storing metadata of attachment </ParamField> </Expandable> </ParamField> </Expandable> </ParamField> <ParamField type="integer"> Sends a message to chat. Set send\_to\_chat=1 to perform. </ParamField> <ParamField type="integer"> Mark messages to support read/delivered statuses. Set `markable=1` to perform. </ParamField> <ParamField type="string"> Name of a custom field. Chat message can be extended with additional fields and contain any other user key-value custom parameters. Can be many 1..N. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="_id" type="string" /> <ResponseField name="attachments" type="array"> <Expandable title="properties"> <ResponseField name="type" type="string" /> <ResponseField name="id" type="string" /> </Expandable> </ResponseField> <ResponseField name="chat_dialog_id" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="date_sent" type="integer" /> <ResponseField name="delivered_ids" type="array" /> <ResponseField name="markable" type="integer" /> <ResponseField name="message" type="string" /> <ResponseField name="read_ids" type="array" /> <ResponseField name="recipient_id" type="integer" /> <ResponseField name="sender_id" type="integer" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="read" type="integer" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "chat_dialog_id":"5d75327ba28f9a0f32cb0f99", "message":"look at this photos", "send_to_chat":1, "markable":1, "attachments":{ "0":{ "type":"image", "id":"47863" }, "1":{ "type":"image", "id":"47864" } } }' \ https://api.quickblox.com/chat/Message.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "_id": "5d76b657a28f9a0cc7cb0f99", "attachments": [ { "type": "image", "id": "47863" }, { "type": "image", "id": "47864" } ], "chat_dialog_id": "5d75327ba28f9a0f32cb0f99", "created_at": null, "date_sent": 1568061015, "delivered_ids": [], "markable": 1, "message": "look at this photos", "read_ids": [], "recipient_id": 0, "sender_id": 96830631, "updated_at": null, "read": 0 } ``` ```json 404 theme={null} { "errors": [ "The recipient wasn't found" ] } ``` </ResponseExample> # Create Multi Records Source: https://docs.quickblox.com/reference/create-multi-records POST https://api.quickblox.com/data/{class_name}/multi.json Create multiple records. #### Recipes <AccordionGroup> <Accordion title="Create multiple records with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "record":{ "0":{ "score_value":3021, "progress":"0.42", "expert_mode":false, "game_mode_name":"4x4", "date":"2019-09-21 21:25:00", "completed_levels":[ 1, 2, 3, 4, 5, 6 ] }, "1":{ "score_value":1211, "progress":"0.32", "expert_mode":true, "game_mode_name":"deathmatch", "user_location":"21.23, 15.32", "date":"2019-09-20 15:21:00", "completed_levels":[ 1, 2, 3, 4, 5 ] } } }' \ "https://api.quickblox.com/data/ScoreTable/multi.json" ``` </Step> <Step title="2. As a result, the API returns the newly created records."> ```json theme={null} { "class_name": "ScoreTable", "items": [ { "_id": "5d867291a0eb473ed96099c4", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4, 5, 6 ], "created_at": 1569092241, "date": "2019-09-21T21:25:00Z", "expert_mode": false, "game_mode_name": "4x4", "progress": 0.42, "score_value": 3021, "updated_at": 1569092241, "user_avatar": null, "user_id": 96753878, "user_location": null, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } }, { "_id": "5d867291a0eb473ed96099c5", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4, 5 ], "created_at": 1569092241, "date": "2019-09-20T15:21:00Z", "expert_mode": true, "game_mode_name": "deathmatch", "progress": 0.32, "score_value": 1211, "updated_at": 1569092241, "user_avatar": null, "user_id": 96753878, "user_location": [ 21.23, 15.32 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ] } ``` </Step> </Steps> </Accordion> </AccordionGroup> **Allowed permissions** | Permission | Syntax | Example | | ------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Open | permissions.\<CRUD\_operation>.access | permissions.read.access=open | | Owner | permissions.\<CRUD\_operation>.access | permissions.read.access=owner | | Open for users IDs | permissions.\<CRUD\_operation>.access=open\_for\_users\_ids permissions.\<CRUD\_operation>.ids=id\_1,id\_2,id\_3,… | permissions.update.access=open\_for\_users\_ids permissions.update.ids=3,12 | | Open for groups | permissions.\<CRUD\_operation>.access=open\_for\_groups permissions.\<CRUD\_operation>.groups=group\_name\_1,group\_name\_2 | permissions.delete.access=open\_for\_groups permissions.delete.groups=experience,rate | #### Path Parameters <ParamField type="string"> A class name of the parent custom object. </ParamField> #### Body Parameters <ParamField type="object"> <Expandable title="properties"> <ParamField type="object"> A number of the record in the query. Begin with 1 to N, where N is the number of the last record. <Expandable title="properties"> <ParamField type="string"> Set value to the field defined in Custom Object class. Can be many 1..N. </ParamField> <ParamField type="string"> Record permissions. Format: <br /> `permission.<CRUD_operation>.access=<value>` <br /> `permission.<CRUD_operation>.<option>=<value>` CRUD operations: `create`, `read`, `update`, `delete`. <br /> CRUD access values: `open`, `owner`, `open_for_users_ids`, `open_for_groups`. <br /> CRUD options: `ids`, `groups`. </ParamField> </Expandable> </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="class_name" type="string" /> <ResponseField name="items" type="array of objects" /> <Expandable title="properties"> <ResponseField name="_id" type="string" /> <ResponseField name="_parent_id" type="string" /> <ResponseField name="created_at" type="integer" /> <ResponseField name="updated_at" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="permissions" type="object"> <Expandable title="properties"> <ResponseField name="read" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="update" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="delete" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Expandable> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 199b1d9ec0dedeb19d81283b3b2a32b80f012bba" \ -d '{ "record":{ "0":{ "score_value":3021, "progress":"0.42", "expert_mode":false, "game_mode_name":"4x4", "date":"2019-09-21 21:25:00", "completed_levels":[ 1, 2, 3, 4, 5, 6 ] }, "1":{ "score_value":1211, "progress":"0.32", "expert_mode":true, "game_mode_name":"deathmatch", "user_location":"21.23, 15.32", "date":"2019-09-20 15:21:00", "completed_levels":[ 1, 2, 3, 4, 5 ] } } }' \ "https://api.quickblox.com/data/ScoreTable/multi.json" ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "class_name": "ScoreTable", "items": [ { "_id": "5d867291a0eb473ed96099c4", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4, 5, 6 ], "created_at": 1569092241, "date": "2019-09-21T21:25:00Z", "expert_mode": false, "game_mode_name": "4x4", "progress": 0.42, "score_value": 3021, "updated_at": 1569092241, "user_avatar": null, "user_id": 96753878, "user_location": null, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } }, { "_id": "5d867291a0eb473ed96099c5", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4, 5 ], "created_at": 1569092241, "date": "2019-09-20T15:21:00Z", "expert_mode": true, "game_mode_name": "deathmatch", "progress": 0.32, "score_value": 1211, "updated_at": 1569092241, "user_avatar": null, "user_id": 96753878, "user_location": [ 21.23, 15.32 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ] } ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to do this operation" ] } ``` </ResponseExample> # Create Record Source: https://docs.quickblox.com/reference/create-record POST https://api.quickblox.com/data/{class_name}.json Create a new record of particular class. #### Recipes <AccordionGroup> <Accordion title="Create record"> <Steps> <Step title="1. Set record fields."> You can set as many fields as you need. ```curl {5-15} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 199b1d9ec0dedeb19d81283b3b2a32b80f012bba" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ] }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="2. Set a class."> Set a class for a new record. Here, the `ScoreTable` class is specified. ```curl {17} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 199b1d9ec0dedeb19d81283b3b2a32b80f012bba" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ] }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="3. As a result, the API returns a new record."> ```json theme={null} { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "deathmatch", "progress": 0.12, "score_value": 665, "updated_at": 1569090387, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Create record with permissions"> <Steps> <Step title="1. Set record fields"> You can set as many fields as you need for a new record ```curl {5-15} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 199b1d9ec0dedeb19d81283b3b2a32b80f012bba" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="2. Use a `permissions` parameter to set operations allowed and access levels"> ```curl {16} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 199b1d9ec0dedeb19d81283b3b2a32b80f012bba" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], "permissions": {} }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="3. Set CRUD operations"> Set CRUD operations that can be performed on a record. Here, the `read`, and `update` operations are set. ```curl {17-18} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 199b1d9ec0dedeb19d81283b3b2a32b80f012bba" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], "permissions": { "read": {}, "update": {} } }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="4. Set access levels"> Set an access level for a record. Here, the `owner`, and `open_for_groups` access levels are set. ```curl {18,21} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 199b1d9ec0dedeb19d81283b3b2a32b80f012bba" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], "permissions": { "read": { "access":"owner", }, "update": { "access":"open_for_groups" } } }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="5. Set a user group"> Set a user group to determine who can make a required operation. Here, users with the `world` tag can make a required operation. ```curl {22-24} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 199b1d9ec0dedeb19d81283b3b2a32b80f012bba" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], "permissions": { "read": { "access":"owner", }, "update": { "access":"open_for_groups", "user_groups":[ "world" ] } } }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="6. As a result, the API returns a new record."> ```json theme={null} { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "deathmatch", "progress": 0.12, "score_value": 665, "updated_at": 1569090387, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "owner" }, "update": { "access": "open_for_groups", "user_groups": [ "world" ] }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Create record with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ] }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="2. Set record fields."> You can set as many fields as you need. ```curl {5-15} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ] }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="3. Set a class."> Set a class for a new record. Here, the `ScoreTable` class is specified. ```curl {17} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ] }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="4. As a result, the API returns a new record."> ```json theme={null} { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "deathmatch", "progress": 0.12, "score_value": 665, "updated_at": 1569090387, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Create record with permissions with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="2. Set record fields"> You can set as many fields as you need for a new record ```curl {5-15} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="3. Use a `permissions` parameter to set operations allowed and access levels"> ```curl {16} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], "permissions": {} }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="4. Set CRUD operations"> Set CRUD operations that can be performed on a record. Here, the `read`, and `update` operations are set. ```curl {17-18} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], "permissions": { "read": {}, "update": {} } }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="5. Set access levels"> Set an access level for a record. Here, the `owner`, and `open_for_groups` access levels are set. ```curl {18,21} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], "permissions": { "read": { "access":"owner", }, "update": { "access":"open_for_groups" } } }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="6. Set a user group"> Set a user group to determine who can make a required operation. Here, users with the `world` tag can make a required operation. ```curl {22-24} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ], "permissions": { "read": { "access":"owner", }, "update": { "access":"open_for_groups", "user_groups":[ "world" ] } } }' \ "https://api.quickblox.com/data/ScoreTable.json" ``` </Step> <Step title="7. As a result, the API returns a new record."> ```json theme={null} { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "deathmatch", "progress": 0.12, "score_value": 665, "updated_at": 1569090387, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "owner" }, "update": { "access": "open_for_groups", "user_groups": [ "world" ] }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> </AccordionGroup> To create a new record use a `POST` request with `application/json` or `application/x-www-form-urlencoded` content type of the POST body. Received values will be cast according to the class defined in QuickBlox Dashboard. The field that is present in the class but **not** specified in the POST request will have a `null` value. **Allowed permissions** | Permission | Syntax | Example | | ------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Open | permissions.\<CRUD\_operation>.access | permissions.read.access=open | | Owner | permissions.\<CRUD\_operation>.access | permissions.read.access=owner | | Open for users IDs | permissions.\<CRUD\_operation>.access=open\_for\_users\_ids permissions.\<CRUD\_operation>.ids=id\_1,id\_2,id\_3,… | permissions.update.access=open\_for\_users\_ids permissions.update.ids=3,12 | | Open for groups | permissions.\<CRUD\_operation>.access=open\_for\_groups permissions.\<CRUD\_operation>.groups=group\_name\_1,group\_name\_2 | permissions.delete.access=open\_for\_groups permissions.delete.groups=experience,rate | #### Path Parameters <ParamField type="string"> Custom object class name.</ParamField> #### Body Parameters <ParamField type="string"> Set value to the field defined in Custom Object class. Can be many 1..N. </ParamField> <ParamField type="string"> Record permissions. Format: <br /> `permission.<CRUD_operation>.access=<value>` <br /> `permission.<CRUD_operation>.<option>=<value>` CRUD operations: `create`, `read`, `update`, `delete`. <br /> CRUD access values: `open`, `owner`, `open_for_users_ids`, `open_for_groups`. <br /> CRUD options: `ids`, `groups`. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="_id" type="string" /> <ResponseField name="_parent_id" type="string" /> <ResponseField name="created_at" type="integer" /> <ResponseField name="updated_at" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="permissions" type="object"> <Expandable title="properties"> <ResponseField name="read" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="update" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="delete" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 199b1d9ec0dedeb19d81283b3b2a32b80f012bba" \ -d '{ "score_value":665, "progress":"0.12", "expert_mode":false, "game_mode_name":"deathmatch", "user_location":"11.13, 45.52", "date":"2019-09-21 21:21:00", "completed_levels":[ 1, 2, 3 ] }' \ https://api.quickblox.com/data/ScoreTable.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "deathmatch", "progress": 0.12, "score_value": 665, "updated_at": 1569090387, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` ```json 400 theme={null} {} ``` </ResponseExample> # Create Session Source: https://docs.quickblox.com/reference/create-session POST https://api.quickblox.com/session.json Creates an application or user session. #### Recipes <AccordionGroup> <Accordion title="Create application session"> <Steps> <Step title="1. Set your Application ID"> Get the `application_id` from the **Dashboard => YOUR\_APP => Overview section**. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730" }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="2. Set your Authorization Key"> Get the `auth_key` from the **Dashboard => YOUR\_APP => Overview section**. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="3. Set a timestamp"> Get a `timestamp` value in the Unix Timestamp. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "timestamp":"1567349756", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="4. Set a nonce"> Set a `nonce` value which is a unique random number that can't be sent twice. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "timestamp":"1567349756", "nonce":"7334", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="5. Set a signature"> Generate a `signature` value. Use [this guide](/reference/authentication#signature-generation) to learn how to generate a signature. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "timestamp":"1567349756", "nonce":"7334", "signature":"e6e603c251a569e70a2f27a8c71b5017e81e05d5" }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="6. As a result, the API returns a newly created application session." /> </Steps> </Accordion> <Accordion title="Create user session"> <Steps> <Step title="1. Set your Application ID"> Get the `application_id` from the **Dashboard => YOUR\_APP => Overview section**. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730" }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="2. Set your Authorization Key"> Get the `auth_key` from the **Dashboard => YOUR\_APP => Overview section**. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"gqneCwWtz8dqF4F", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="3. Set a timestamp"> Get a `timestamp` value in the Unix Timestamp. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "timestamp":"1567349717", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="4. Set a nonce"> Set a `nonce` value which is a unique random number that can't be sent twice. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "timestamp":"1567349756", "nonce":"7497", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="5. Set a signature"> Generate a `signature` value. Use [this guide](/reference/authentication#signature-generation) to learn how to generate a signature. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "timestamp":"1567349756", "nonce":"7334", "signature":"1924a2c84b96347d66b6b2c8489a3cf1d35a8d74", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="6. Set a login of the user"> Set a `login` value to identify a user on the server. ```curl {10} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "timestamp":"1567349756", "nonce":"7334", "signature":"1924a2c84b96347d66b6b2c8489a3cf1d35a8d74", "user":{ "login":"Mike", } }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="7. Set a password of the user"> Set a `password` value to identify a user on the server. ```curl {11} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "timestamp":"1567349756", "nonce":"7334", "signature":"1924a2c84b96347d66b6b2c8489a3cf1d35a8d74", "user":{ "login":"Mike", "password":"!mike123" } }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="8. As a result, the API returns a newly created user session." /> </Steps> </Accordion> <Accordion title="Create session with Firebase phone number"> <Steps> <Step title="1. Set your Application ID"> Get the `application_id` from the **Dashboard => YOUR\_APP => Overview section**. ```curl {4} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"71116", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="2. Set your Authorization Key"> Get the `auth_key` from the **Dashboard => YOUR\_APP => Overview section**. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"RTydVt3ktSV3mpK", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="3. Set a Firebase user ID token"> Set a Firebase user ID token using the `firebase_phone[access_token]`. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "firebase_phone[access_token]":"eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc3MTBiMDE3ZmQ5YjcxMWUwMDljNmMzNmIwNzNiOGE2N2NiNjgyMTEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcXVldWUtZGVtby1jbGllbnQiLCJhdWQiOiJxdWV1ZS1kZW1vLWNsaWVudCIsImF1dGhfdGltZSI6MTYyNjQzMjg5OSwidXNlcl9pZCI6InFhZnNjMHpnOHNVSktvdFJkb1J1eUx1UXdiRjMiLCJzdWIiOiJxYWZzYzB6ZzhzVUpLb3RSZG9SdXlMdVF3YkYzIiwiaWF0IjoxNjI2NDMyOTAwLCJleHAiOjE2MjY0MzY1MDAsInBob25lX251bWJlciI6IiszODA5Mzc2MzcyNTMiLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7InBob25lIjpbIiszODA5Mzc2MzcyNTMiXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwaG9uZSJ9fQ.ORDJTSGTicsvAeJ-eUQcDvt5Jv-y1c3HteOlzLApTZZ2C-nJZOB4RFpvoviIQwH53PFANB5iXTHBw1a5XBKROeRYEyu8YLg6RsH0wt5f5_hzGsUisgeFleDrz9i3V7NqkITqz2ADcJrWzwF-YDj5TbzCTlHmgeciiuViVbLqyFSKfyYrvFjM7x1IvJYUNVD3XGIq33128eaqHRuWfiaW5PC9ZdvfdOF5UhNpBx330TfM1FV9gNBQxd3cmxCE-bD1kvHQWLk6z8XKTNWZag718_Gf7MVmhqk-bC_KtwQ7IGVMRjPgCdNbA8H1btIcofD2iUVtzTew4et7leR6ujCAcA", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="4. Set an ID of the Firebase project"> Set an ID of the Firebase project using the `firebase_phone[project_id]`. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "firebase_phone[access_token]":"eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc3MTBiMDE3ZmQ5YjcxMWUwMDljNmMzNmIwNzNiOGE2N2NiNjgyMTEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcXVldWUtZGVtby1jbGllbnQiLCJhdWQiOiJxdWV1ZS1kZW1vLWNsaWVudCIsImF1dGhfdGltZSI6MTYyNjQzMjg5OSwidXNlcl9pZCI6InFhZnNjMHpnOHNVSktvdFJkb1J1eUx1UXdiRjMiLCJzdWIiOiJxYWZzYzB6ZzhzVUpLb3RSZG9SdXlMdVF3YkYzIiwiaWF0IjoxNjI2NDMyOTAwLCJleHAiOjE2MjY0MzY1MDAsInBob25lX251bWJlciI6IiszODA5Mzc2MzcyNTMiLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7InBob25lIjpbIiszODA5Mzc2MzcyNTMiXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwaG9uZSJ9fQ.ORDJTSGTicsvAeJ-eUQcDvt5Jv-y1c3HteOlzLApTZZ2C-nJZOB4RFpvoviIQwH53PFANB5iXTHBw1a5XBKROeRYEyu8YLg6RsH0wt5f5_hzGsUisgeFleDrz9i3V7NqkITqz2ADcJrWzwF-YDj5TbzCTlHmgeciiuViVbLqyFSKfyYrvFjM7x1IvJYUNVD3XGIq33128eaqHRuWfiaW5PC9ZdvfdOF5UhNpBx330TfM1FV9gNBQxd3cmxCE-bD1kvHQWLk6z8XKTNWZag718_Gf7MVmhqk-bC_KtwQ7IGVMRjPgCdNbA8H1btIcofD2iUVtzTew4et7leR6ujCAcA", "firebase_phone[project_id]:"33444545", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="5. Set a nonce"> Set a `nonce` value which is a unique random number that can't be sent twice. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "firebase_phone[access_token]":"eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc3MTBiMDE3ZmQ5YjcxMWUwMDljNmMzNmIwNzNiOGE2N2NiNjgyMTEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcXVldWUtZGVtby1jbGllbnQiLCJhdWQiOiJxdWV1ZS1kZW1vLWNsaWVudCIsImF1dGhfdGltZSI6MTYyNjQzMjg5OSwidXNlcl9pZCI6InFhZnNjMHpnOHNVSktvdFJkb1J1eUx1UXdiRjMiLCJzdWIiOiJxYWZzYzB6ZzhzVUpLb3RSZG9SdXlMdVF3YkYzIiwiaWF0IjoxNjI2NDMyOTAwLCJleHAiOjE2MjY0MzY1MDAsInBob25lX251bWJlciI6IiszODA5Mzc2MzcyNTMiLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7InBob25lIjpbIiszODA5Mzc2MzcyNTMiXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwaG9uZSJ9fQ.ORDJTSGTicsvAeJ-eUQcDvt5Jv-y1c3HteOlzLApTZZ2C-nJZOB4RFpvoviIQwH53PFANB5iXTHBw1a5XBKROeRYEyu8YLg6RsH0wt5f5_hzGsUisgeFleDrz9i3V7NqkITqz2ADcJrWzwF-YDj5TbzCTlHmgeciiuViVbLqyFSKfyYrvFjM7x1IvJYUNVD3XGIq33128eaqHRuWfiaW5PC9ZdvfdOF5UhNpBx330TfM1FV9gNBQxd3cmxCE-bD1kvHQWLk6z8XKTNWZag718_Gf7MVmhqk-bC_KtwQ7IGVMRjPgCdNbA8H1btIcofD2iUVtzTew4et7leR6ujCAcA", "firebase_phone[project_id]:"33444545", "nonce":"9823", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="6. Set a social network provider"> Use a `provider` param to set a social network provider. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "firebase_phone[access_token]":"eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc3MTBiMDE3ZmQ5YjcxMWUwMDljNmMzNmIwNzNiOGE2N2NiNjgyMTEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcXVldWUtZGVtby1jbGllbnQiLCJhdWQiOiJxdWV1ZS1kZW1vLWNsaWVudCIsImF1dGhfdGltZSI6MTYyNjQzMjg5OSwidXNlcl9pZCI6InFhZnNjMHpnOHNVSktvdFJkb1J1eUx1UXdiRjMiLCJzdWIiOiJxYWZzYzB6ZzhzVUpLb3RSZG9SdXlMdVF3YkYzIiwiaWF0IjoxNjI2NDMyOTAwLCJleHAiOjE2MjY0MzY1MDAsInBob25lX251bWJlciI6IiszODA5Mzc2MzcyNTMiLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7InBob25lIjpbIiszODA5Mzc2MzcyNTMiXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwaG9uZSJ9fQ.ORDJTSGTicsvAeJ-eUQcDvt5Jv-y1c3HteOlzLApTZZ2C-nJZOB4RFpvoviIQwH53PFANB5iXTHBw1a5XBKROeRYEyu8YLg6RsH0wt5f5_hzGsUisgeFleDrz9i3V7NqkITqz2ADcJrWzwF-YDj5TbzCTlHmgeciiuViVbLqyFSKfyYrvFjM7x1IvJYUNVD3XGIq33128eaqHRuWfiaW5PC9ZdvfdOF5UhNpBx330TfM1FV9gNBQxd3cmxCE-bD1kvHQWLk6z8XKTNWZag718_Gf7MVmhqk-bC_KtwQ7IGVMRjPgCdNbA8H1btIcofD2iUVtzTew4et7leR6ujCAcA", "firebase_phone[project_id]:"33444545", "nonce":"9823", "provider":"firebase_phone", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="7. Set a signature"> Generate a `signature` value. Use [this guide](/reference/authentication#signature-generation) to learn how to generate a signature. ```curl {10} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "firebase_phone[access_token]":"eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc3MTBiMDE3ZmQ5YjcxMWUwMDljNmMzNmIwNzNiOGE2N2NiNjgyMTEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcXVldWUtZGVtby1jbGllbnQiLCJhdWQiOiJxdWV1ZS1kZW1vLWNsaWVudCIsImF1dGhfdGltZSI6MTYyNjQzMjg5OSwidXNlcl9pZCI6InFhZnNjMHpnOHNVSktvdFJkb1J1eUx1UXdiRjMiLCJzdWIiOiJxYWZzYzB6ZzhzVUpLb3RSZG9SdXlMdVF3YkYzIiwiaWF0IjoxNjI2NDMyOTAwLCJleHAiOjE2MjY0MzY1MDAsInBob25lX251bWJlciI6IiszODA5Mzc2MzcyNTMiLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7InBob25lIjpbIiszODA5Mzc2MzcyNTMiXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwaG9uZSJ9fQ.ORDJTSGTicsvAeJ-eUQcDvt5Jv-y1c3HteOlzLApTZZ2C-nJZOB4RFpvoviIQwH53PFANB5iXTHBw1a5XBKROeRYEyu8YLg6RsH0wt5f5_hzGsUisgeFleDrz9i3V7NqkITqz2ADcJrWzwF-YDj5TbzCTlHmgeciiuViVbLqyFSKfyYrvFjM7x1IvJYUNVD3XGIq33128eaqHRuWfiaW5PC9ZdvfdOF5UhNpBx330TfM1FV9gNBQxd3cmxCE-bD1kvHQWLk6z8XKTNWZag718_Gf7MVmhqk-bC_KtwQ7IGVMRjPgCdNbA8H1btIcofD2iUVtzTew4et7leR6ujCAcA", "firebase_phone[project_id]:"33444545", "nonce":"9823", "provider":"firebase_phone", "signature":"5277a30edf002d59318ecec2cf3ab17c4bbd3bee", }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="8. Set a timestamp"> Get a `timestamp` value in the Unix Timestamp. ```curl {11} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "firebase_phone[access_token]":"eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc3MTBiMDE3ZmQ5YjcxMWUwMDljNmMzNmIwNzNiOGE2N2NiNjgyMTEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcXVldWUtZGVtby1jbGllbnQiLCJhdWQiOiJxdWV1ZS1kZW1vLWNsaWVudCIsImF1dGhfdGltZSI6MTYyNjQzMjg5OSwidXNlcl9pZCI6InFhZnNjMHpnOHNVSktvdFJkb1J1eUx1UXdiRjMiLCJzdWIiOiJxYWZzYzB6ZzhzVUpLb3RSZG9SdXlMdVF3YkYzIiwiaWF0IjoxNjI2NDMyOTAwLCJleHAiOjE2MjY0MzY1MDAsInBob25lX251bWJlciI6IiszODA5Mzc2MzcyNTMiLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7InBob25lIjpbIiszODA5Mzc2MzcyNTMiXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwaG9uZSJ9fQ.ORDJTSGTicsvAeJ-eUQcDvt5Jv-y1c3HteOlzLApTZZ2C-nJZOB4RFpvoviIQwH53PFANB5iXTHBw1a5XBKROeRYEyu8YLg6RsH0wt5f5_hzGsUisgeFleDrz9i3V7NqkITqz2ADcJrWzwF-YDj5TbzCTlHmgeciiuViVbLqyFSKfyYrvFjM7x1IvJYUNVD3XGIq33128eaqHRuWfiaW5PC9ZdvfdOF5UhNpBx330TfM1FV9gNBQxd3cmxCE-bD1kvHQWLk6z8XKTNWZag718_Gf7MVmhqk-bC_KtwQ7IGVMRjPgCdNbA8H1btIcofD2iUVtzTew4et7leR6ujCAcA", "firebase_phone[project_id]:"33444545", "nonce":"9823", "provider":"firebase_phone", "signature":"5277a30edf002d59318ecec2cf3ab17c4bbd3bee", "timestamp":"1626433392" }' \ https://api.quickblox.com/session.json ``` </Step> <Step title="9. As a result, the API returns a newly created user session." /> </Steps> </Accordion> </AccordionGroup> Learn more about QuickBlox session token in the [Application vs User session token](/reference/authentication#application-vs-user-session-token) section. **Application session** To create an application session you need to pass the required parameters **only**. **User session** To create a user session you need to pass extra parameters together with the required parameters. Take a look at five types of user authentication and their respective extra parameters below: * Login and password. `user[login]` and `user[password]`. * Email and password. `user[email]` and `user[password]`. * Social. `provider=facebook`, `keys[token]`, and `keys[secret]` . * Firebase phone number. `provider=firebase_phone`, `firebase_phone[project_id]`, and `firebase_phone[access_token]`. * Custom identity provider. `user[login]` and `user[password]`. Learn more about custom identity provider authentication from [our guide](/docs/custom-identity-provider). #### Body Parameters <ParamField type="string"> Application identifier. </ParamField> <ParamField type="string"> Authorization Key. </ParamField> <ParamField type="string"> Unix Timestamp. It should not differ from the time provided by NTP more than 60 minutes. We suggest you synchronize time on your devices with NTP service. </ParamField> <ParamField type="string"> Unique Random Value. Requests with the same timestamp and same value for nonce parameter can not be sent twice. </ParamField> <ParamField type="string"> Cryptographic hash. Follow a [signature generation guide](/reference/authentication#signature-generation) to learn how to generate a signature. </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> User login. </ParamField>\ <ParamField type="string"> User email. </ParamField>\ <ParamField type="string"> User password. </ParamField> </Expandable> </ParamField> <ParamField type="string"> Possible values: facebook, firebase\_phone </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> Social network provider access token. </ParamField> <ParamField type="string"> Social network provider access token secret. </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> A unique identifier for your Firebase project. </ParamField> <ParamField type="string"> Firebase user's ID token. </ParamField> </Expandable> </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="session" type="object"> <Expandable title="properties"> <ResponseField name="application_id" type="integer" /> <ResponseField name="created_at" type="string" /> <ResponseField name="id" type="integer" /> <ResponseField name="nonce" type="integer" /> <ResponseField name="token" type="string" /> <ResponseField name="ts" type="integer" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="_id" type="string" /> </Expandable> </ResponseField> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "application_id":"76730", "auth_key":"7quWEh-k6TqghXe", "timestamp":"1567349756", "nonce":"7334", "signature":"e6e603c251a569e70a2f27a8c71b5017e81e05d5" }' \ https://api.quickblox.com/session.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "session": { "application_id": 76730, "created_at": "2019-09-01T14:55:56Z", "id": 1151827623, "nonce": 7334, "token": "c56f382b402fb482086db956064592d3cd012bba", "ts": 1567349756, "updated_at": "2019-09-01T14:55:56Z", "user_id": 0, "_id": "1151827623" } } ``` ```json 401 theme={null} { "errors": [ "Unauthorized" ] } ``` ```json 422 theme={null} { "errors": { "base": [ "Unexpected signature" ] } } ``` </ResponseExample> # Create Subscription Source: https://docs.quickblox.com/reference/create-subscription POST https://api.quickblox.com/subscriptions.json Create device based subscriptions. <Warning> A single user can have up to **10** subscriptions on different devices. </Warning> #### Body Parameters <ParamField type="string"> Declare which notification channels could be used to notify user about events. Possible values are: `apns`, `apns_voip`, `gcm`, and `email`. </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> Determine application mode. It allows conveniently separate development and production modes. Possible values are: `development` and `production`. </ParamField> <ParamField type="string"> A unique identifier for client's application. In iOS, this is a Bundle Identifier. In Android, a package ID. </ParamField> <ParamField type="string"> A device registration token generated by the APNs or GCM/FCM. The token can be unregistered by the APNs or GCM/FCM anytime. In this case, the device should be registered again and obtain a new token. When a new token is obtained, a new subscription should be created. </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> Platform of the device, which is the source of application running. Possible values are: `ios` and `android`. </ParamField> <ParamField type="string"> UDID (Unique Device Identifier) of the device that is the source of application running. This must be any sequence uniquely identifying a particular device. This is needed to support schema: 1 User => Multiple devices. </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response. <Expandable title="array of objects"> <ResponseField name="subscription" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="device" type="object"> <Expandable title="properties"> <ResponseField name="udid" type="string" /> <ResponseField name="client_identification_sequence" type="string" /> <ResponseField name="platform" type="object"> <Expandable title="properties"> <ResponseField name="name" type="string" /> </Expandable> </ResponseField> <ResponseField name="bundle_identifier" type="string" /> </Expandable> </ResponseField> <ResponseField name="notification_channel" type="object"> <Expandable title="notification_channel"> <ResponseField name="name" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Expandable> </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="environment" type="array of strings" /> <ResponseField name="client_identification_sequence" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 1e0bad9d9830c038ea19f12d83fca4e984012bba" \ -d '{ "notification_channel":"apns", "push_token":{ "environment":"development", "client_identification_sequence":"1054929587f29c856fa0137f634cc0a5ec77901f4baaf2f3c1785d05ae114455", "bundle_identifier":"com.quickblox.myawesomeapp" }, "device":{ "platform":"ios", "udid":"2343af3433aa3a4fsv" } }' \ https://api.quickblox.com/subscriptions.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} [ { "subscription": { "id": 59715257, "device": { "udid": "2343af3433aa3a4fsv", "client_identification_sequence": "1054929587f29c856fa0137f634cc0a5ec77901f4baaf2f3c1785d05ae114455", "platform": { "name": "ios" }, "bundle_identifier": "com.quickblox.myawesomeapp" }, "notification_channel": { "name": "apns" } } } ] ``` ```json 422 theme={null} { "errors": { "environment": [ "is not included in the list (production, development)" ], "client_identification_sequence": [ "is invalid" ] } } ``` </ResponseExample> # Create Token with API key Source: https://docs.quickblox.com/reference/create-token-with-api-key POST https://api.quickblox.com/users/{user_id}/tokens Creates user session for a user using an API key <Warning> You can't use the QB token in this request. It will be ignored if you pass the QB-Token header in the request. </Warning> #### Path Parameters <ParamField type="integer"> ID of the user. Generated automatically by the server after user creation.</ParamField> #### Headers <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="token" type="string" /> <ResponseField name="expires_at" type="string" /> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Authorization: ApiKey cf5709d6013fdb7a6787fbeb8340afed8aec4c69" \ https://api.quickblox.com/users/123/tokens ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "token": "c56f382b402fb482086db956064592d3cd012bba", "expires_at": "2023-09-01T14:55:56Z" } ``` ```json 401 theme={null} // Invalid Authorization header { "errors": { "base": [ "Authorization header is invalid." ] } } // Invalid API Key { "errors": { "base": [ "Api Key is not found." ] } } ``` ```json 404 theme={null} { "errors": { "base": [ "User is not found." ] } } ``` </ResponseExample> # Create User Source: https://docs.quickblox.com/reference/create-user POST https://api.quickblox.com/users.json Creates a new user. #### Recipes <AccordionGroup> <Accordion title="Create user with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X POST \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user": { "login": "johndoe", "password":"quickblox", "full_name": "John Doe" } }' \ https://api.quickblox.com/users.json ``` </Step> <Step title="2. Use a `user` parameter to create a new user"> ```curl {5} theme={null} curl -X POST \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user": { "login": "johndoe", "password":"quickblox", "full_name": "John Doe" } }' \ https://api.quickblox.com/users.json ``` </Step> <Step title="3. Set values for the fields"> ```curl {6-8} theme={null} curl -X POST \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user": { "login": "johndoe", "password":"quickblox", "full_name": "John Doe" } }' \ https://api.quickblox.com/users.json ``` </Step> <Step title="4. As a result, the API returns a created user"> ```curl theme={null} curl -X POST \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user": { "login": "johndoe", "password":"quickblox", "full_name": "John Doe" } }' \ https://api.quickblox.com/users.json ``` </Step> </Steps> </Accordion> </AccordionGroup> <Info> Trailing whitespaces in string data (except for the password) will be trimmed. </Info> The request can contain all, some or none of the optional parameters. Login, email, Facebook ID, and the external user ID should not be taken previously. If you want to create a user with some content (for example, with an avatar) you have to create a blob first. The blob is the entity that is a file in a system. To learn how to create a blob, review [this section](/reference/create-file). You can assign tags to the user. The same tags can be used for any number of users. #### Body Parameters <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> User login. Must be **unique**. Only **one** required: `login` or `email`. </ParamField> <ParamField type="string"> User e-mail. Must be **unique**. Only **one** required: `login` or `email`. </ParamField> <ParamField type="string"> User password. </ParamField> <ParamField type="integer"> ID of the file/blob. Generated automatically by the server after file/blob creation (for example, user photo). </ParamField> <ParamField type="integer"> ID of the user in the external system (<Tooltip>External User ID</Tooltip>.). Must be **unique**. </ParamField> <ParamField type="string"> ID of the user's Facebook account. Must be **unique**. </ParamField> <ParamField type="string"> User's full name. </ParamField> <ParamField type="string"> User's phone. </ParamField> <ParamField type="string"> User's website. </ParamField> <ParamField type="string"> User's additional info. </ParamField> <ParamField type="string"> User tags. </ParamField> <ParamField type="boolean"> ~~Marketing info.~~ </ParamField> <ParamField type="boolean"> ~~Marketing info.~~ </ParamField> <ParamField type="boolean"> ~~GDPR info.~~ </ParamField> <ParamField type="string"> ~~GDPR info.~~</ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="full_name" type="array of strings"> Errors related to full name </ResponseField> <ResponseField name="email" type="array of strings"> Errors related to email </ResponseField> <ResponseField name="login" type="array of strings"> Errors related to login </ResponseField> <ResponseField name="password" type="array of strings"> Errors related to password </ResponseField> <ResponseField name="facebook_id" type="array of strings"> Errors related to Facebook ID </ResponseField> <ResponseField name="external_user_id" type="array of strings"> Errors related to external user ID </ResponseField> <ResponseField name="phone" type="array of strings"> Errors related to phone number </ResponseField> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: cf5709d6013fdb7a6787fbeb8340afed8aec4c69" \ -d '{ "user":{ "login":"Peter", "password":"!Peter009", "email":"peter@gmail.com", "external_user_id":1236672, "facebook_id":"8887366", "full_name":"Peter Smith", "phone":"+73123555", "website":"https://mysite.com", "tag_list":"marketing,seo", "custom_data":"lead of marketing team" } }' \ https://api.quickblox.com/users.json ``` </RequestExample> <ResponseExample> ```json 201 theme={null} { "user": { "id": 96752798, "full_name": "Peter Smith", "email": "peter@gmail.com", "login": "Peter", "phone": "+73123555", "website": "https://mysite.com", "created_at": "2019-08-27T20:19:08Z", "updated_at": "2019-08-27T20:19:08Z", "last_request_at": null, "external_user_id": 1236672, "facebook_id": "8887366", "blob_id": null, "custom_data": "lead of marketing team", "age_over16": false, "allow_statistics_analysis": false, "allow_sales_activities": false, "parents_contacts": "", "user_tags": "marketing,seo" } } ``` ```json 400 theme={null} { "code": null, "message": "No data was provided" } ``` ```json 422 theme={null} { "errors": { "full_name": [ "is invalid", "is too long (maximum is 200 characters)" ], "email": [ "should look like an email address" ], "login": [ "has already been taken" ], "password": [ "is too short (minimum is 8 characters)" ], "facebook_id": [ "should be hexadecimal or decimal number" ], "external_user_id": [ "must be less than 4 bytes" ], "phone": [ "is not a number" ] } } ``` </ResponseExample> # Custom Objects Source: https://docs.quickblox.com/reference/custom-objects Custom Objects module provides flexibility to define any data structure (schema) you need, build 1-to-many relations between schemas and control permissions for all operations made on data. Schema is defined in QuickBlox Dashboard. There are two key concepts in Custom Objects: * **Class** represents your schema and contains field names and types. * **Record** represents data you put into your schema. **Class** and **Record** are similar to table and row in relational database. Every class in Custom Object module comes with five mandatory predefined fields: `_id`, `user_id`, `parent_id`, `created_at`, and `updated_at`. Allowed data types: **Integer** (or Array of Integer); **String** (or Array of String); **Float** (or Array of Float); **Boolean** (or Array of Boolean); **Location** (Array of \[\<longitude>, \<latitude>]); **File**; **Date**. For example, you want to store a simple calendar that stores the event title and event date. In this case, you can create a **Calendar** class with following fields: `event_title` and `event_date`. Calendar records will have the following structure: | Field name | Type | | ------------- | ------- | | \_id\* | String | | user\_id\* | Integer | | parent\_id\* | Integer | | event\_title | String | | event\_date | Date | | created\_at\* | Integer | | updated\_at\* | Integer | Calendar data will look like: | \_id\* | user\_id\* | parent\_id\* | event\_title | event\_date | created\_at\* | updated\_at\* | | ------------------------ | ---------- | ------------ | ---------------- | -------------------- | ------------- | ------------- | | 5d866b53a28f9a5ad51cb562 | 96753878 | null | Meet John | 2019-12-25T14:00:00Z | 1576629761 | 1576629761 | | 5d866b53a28f9a5ad51cb563 | 96753878 | null | Visit my dentist | 2019-09-25T15:00:00Z | 1577282400 | 1577282400 | # Delete File Source: https://docs.quickblox.com/reference/custom-objects-delete-file DELETE https://api.quickblox.com/data/{class_name}/{custom_object_record_id}/file.json Delete the content of file fields. #### Recipes <AccordionGroup> <Accordion title="Delete file with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X DELETE \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d "field_name=avatar" \ https://api.quickblox.com/data/UserProfileClass/5d863a78a0eb474dae6099c6/file.json ``` </Step> <Step title="2. As a result, the file is removed from the record."> ```text theme={null} HTTP/1.1 200 OK ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> <ParamField type="string"> ID of the custom object record. Generated automatically by the server after record creation. </ParamField> #### Body Parameters <ParamField type="string"> Custom object record field which contains a file. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d "field_name=avatar" \ https://api.quickblox.com/data/UserProfileClass/5d863a78a0eb474dae6099c6/file.json ``` </RequestExample> <ResponseExample> ```json 403 theme={null} { "errors": [ "Wrong permission" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # Dashboard Source: https://docs.quickblox.com/reference/dashboard To start using Custom Objects module, create a class: 1. Go to [QuickBlox Dashboard](https://admin.quickblox.com/signin). 2. Follow **Custom Objects => Add new class** direction. As a result, **Add new class** popup will appear. 3. Enter a class name, add any fields you want. <Frame> <img alt="1454" /> </Frame> 4. Click **Create class button** and a new class will be created. <Frame> <img alt="1690" /> </Frame> To add a record, do the following: 1. Click **Add record** button and **Add new record** popup will appear 2. Fill any fields you want. 3. Click **Add record button** and a new record will be added and shown in the table. # Delete Dialog Source: https://docs.quickblox.com/reference/delete-dialog DELETE https://api.quickblox.com/chat/Dialog/{chat_dialog_id},{chat_dialog_id}.json Delete a chat dialog. #### Recipes <AccordionGroup> <Accordion title="Delete dialogs from your own chat history"> <Steps> <Step title="1. Set dialogs IDs that should be removed"> Specify as many `dialogs IDs` as you need via comma. ```curl {4} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> <Step title="2. As a result, the dialogs will be removed for a user who has sent the request, not for all dialog participants."> ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> </Steps> </Accordion> <Accordion title="Delete dialogs from all chat histories"> <Steps> <Step title="1. Specify if you want to delete dialogs for all dialog participants"> You can delete dialogs from the histories of all dialog participants. Just set the `force=1` and dialogs will be removed for all users participating in the dialog. ```curl {4} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d "force=1" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> <Step title="2. Set dialogs IDs that should be removed"> Specify as many dialogs IDs as you need via comma. ```curl {5} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d "force=1" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> <Step title="3. As a result, the API will return the `SuccessfullyDeleted` response."> If you're not the owner of the dialog, the API will return the following message: "WrongPermissions". This means that a user doesn't have permissions to delete dialogs from all users' histories. ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d "force=1" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> </Steps> </Accordion> <Accordion title="Delete dialogs from specific user chat history with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> <Step title="3. Set dialogs IDs that should be removed"> Specify as many dialogs IDs as you need via comma. ```curl {5} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> <Step title="4. As a result, the dialogs will be removed for a user who has sent the request, not for all dialog participants."> ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> </Steps> </Accordion> <Accordion title="Delete dialogs with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> <Step title="3. Set dialogs IDs that should be removed"> Specify as many dialogs IDs as you need via comma. ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> <Step title="4. As a result, the dialogs will be removed for all dialog participants."> ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Dialog/5d75626ea28f9a1490cb0f98,5d7562d4a28f9a1694cb0f9b,5d7562d4a2123a1694cb0f9b.json ``` </Step> </Steps> </Accordion> </AccordionGroup> Each user from a dialog’s `occupant_ids` field can remove the dialog. It does not mean that this dialog will be removed completely for all users in this dialog. It will be removed only for a current user. To completely remove a dialog, pass `force=1`. Only the **owner** can do it. #### Path Parameters <ParamField type="string"> ID of the dialog to which a current message is connected. Generated automatically by the server after message creation</ParamField> #### Body Parameters <ParamField type="integer">Delete a dialog for everyone. Set `force=1` to perform. Only the owner can do it. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="type" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Dialog/53aabe15e4b077ddd43e7fd6.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} // Delete Dialog (one) Empty body ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to perform this operation" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # Delete Event Source: https://docs.quickblox.com/reference/delete-event DELETE https://api.quickblox.com/events/{event_id}.json Delete event. #### Path Params <ParamField type="integer">ID of the event. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -i -X DELETE \ -H "QB-Token: 37d3a10a1843ac780cb6f2c683bddad2b0012bba" \ https://api.quickblox.com/events/30541483.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 403 theme={null} { "errors": { "base": [ "You are not owner of this object" ] } } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find Event::Base with 'id'=123" } ``` </ResponseExample> # Delete File Source: https://docs.quickblox.com/reference/delete-file DELETE https://api.quickblox.com/blobs/{blob_id}.json Delete a file by ID. #### Path Params <ParamField type="integer">ID of the file/blob. Generated automatically by the server after file/blob creation.</ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -i -X DELETE \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ https://api.quickblox.com/blobs/9505829.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} {} ``` ```json 403 theme={null} { "errors": [ "Forbidden" ] } ``` ```json 404 theme={null} Empty body ``` </ResponseExample> # Delete Message Source: https://docs.quickblox.com/reference/delete-message DELETE https://api.quickblox.com/chat/Message/{chat_message_id},{chat_message_id}.json Remove a message for a current user. ### Recipes <AccordionGroup> <Accordion title="Delete messages from all chat histories"> <Steps> <Step title="1. Specify if you want to delete messages from all chat histories"> Set a `force` parameter to `1` to delete messages from all chat histories. ```curl {4} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d "force=1" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> <Step title="2. Set the IDs of the messages"> Specify the IDs of the messages via comma to refer to specific messages that should be deleted from all users' histories. ```curl {5} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d "force=1" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> <Step title="3. As a result, the messages will be deleted from all chat histories."> If you're not the owner of the dialog, the API will return the following message: "WrongPermissions". This means that a user doesn't have permissions to delete a message from all users' histories. ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d "force=1" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> </Steps> </Accordion> <Accordion title="Delete messages from your own chat history"> <Steps> <Step title="1. Set the IDs of the messages"> Specify the IDs of the messages via comma to refer to specific messages that should be deleted from your own chat history. ```curl {4} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> <Step title="2. As a result, the messages are removed for a user who has sent the request without affecting chat histories of other users."> ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> </Steps> </Accordion> <Accordion title="Delete messages from specific user chat history with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> <Step title="3. Set the IDs of the messages"> Specify the IDs of the messages via comma to refer to specific messages that should be deleted from the chat history of the user with ID **123**. ```curl {5} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> <Step title="4. As a result, the messages are removed for a user who has sent the request without affecting chat histories of other users."> ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> </Steps> </Accordion> <Accordion title="Delete messages from all chat histories"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> <Step title="2. Set the IDs of the messages"> Specify the IDs of the messages via comma to refer to specific messages that should be deleted from all users' histories. ```curl {4} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> <Step title="3. As a result, the messages will be deleted from all chat histories."> If the API key is not related to the same application as the messages, the API will return the following message: "WrongPermissions". This means that a user doesn't have permission to delete a message. ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </Step> </Steps> </Accordion> <Accordion title="Hard delete messages from chat history with API key"> <Steps> <Step title="1. Specify if you want to delete messages from all chat histories"> Set a `force` parameter to `1` to delete messages from all chat histories. ```curl {5} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json -d "force=1" \ ``` </Step> <Step title="2. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json -d "force=1" \ ``` </Step> <Step title="3. Set the IDs of the messages"> Specify the IDs of the messages via comma to refer to specific messages that should be deleted from all users' histories. ```curl {4} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json -d "force=1" \ ``` </Step> <Step title="4. As a result, the messages will be deleted from all chat histories."> If the API key is not related to the same application as the messages, the API will return the following message: "WrongPermissions". This means that a user doesn't have permission to delete a message. ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json -d "force=1" \ ``` </Step> </Steps> </Accordion> </AccordionGroup> Any user in the dialog’s `occupants_ids` is able to remove a message from the dialog. The message will **only** be removed for the **current user.** The message will still be viewable in the chat history for all other users in the dialog. You may be wondering why this request only deletes the message for the current user. It is to allow deletion functionality within your app and ensure the message does not reappear in the user chat history, but without affecting the history of other users. It is possible to completely remove a message by the **owner** - pass `force=1`. Starting from version 2.52.0, **admins** can also completely remove messages of other users using `force=1`. ### Path Parameters <ParamField type="string"> ID of the message. Generated automatically by the server after message creation. </ParamField> ### Body Parameters <ParamField type="integer"> Delete message for everyone. Set `force=1` to perform. Only the owner can do it. Starting from v2.52.0, for dialog `type=1` (PUBLIC\_GROUP) and `type=2` (GROUP), admins can also delete messages of other occupants.</ParamField> ### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> ### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="SuccessfullyDeleted" type="object"> <Expandable title="properties"> <ResponseField name="ids" type="array of strings" /> </Expandable> </ResponseField> <ResponseField name="NotFound" type="object"> <Expandable title="properties"> <ResponseField name="ids" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d76b949a0eb4721f80b72ab,5d76b657a28f9a0cc7cb0f99,5d76b6sdfff9a0cc7cb023.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "SuccessfullyDeleted": { "ids": [ "5d76b657a28f9a0cc7cb0f99", "5d76b949a0eb4721f80b72ab" ] }, "NotFound": { "ids": [ "5d76b6sdfff9a0cc7cb023" ] } } ``` ```json 404 theme={null} // All messages don't exist { "errors": ["The resource wasn't found"] } ``` </ResponseExample> # Delete Records Source: https://docs.quickblox.com/reference/delete-records DELETE https://api.quickblox.com/data/{class_name}/{custom_object_record_id},{custom_object_record_id}.json Delete records by IDs. #### Recipes <AccordionGroup> <Accordion title="Delete many records"> <Steps> <Step title="1. Set records IDs"> Set IDs of records that should be deleted via comma. ```curl {3} theme={null} curl -X DELETE \ -H "QB-Token: d9c91a69e552145f6fe24dbb40b50ac575012bba" \ "https://api.quickblox.com/data/MyScoreTable/5d87d328a28f9a545f1cb558,51c9ab92535c12951b0032dd,5d87d354a28f9a59df1cb557.json" ``` </Step> <Step title="2. As a result, the API returns a '200 OK' status code which implies that records were deleted."> ```curl theme={null} 200 OK ``` </Step> </Steps> </Accordion> <Accordion title="Delete one record"> <Steps> <Step title="1. Set a record ID"> Set the ID of the record that should be deleted. ```curl {3} theme={null} curl -X DELETE \ -H "QB-Token: d9c91a69e552145f6fe24dbb40b50ac575012bba" \ "https://api.quickblox.com/data/ScoreTable/5d87d2f4a28f9a48631cb55b.json" ``` </Step> <Step title="2. As a result, the API returns a '200 OK' status code which implies that a record was deleted."> ```curl theme={null} 200 OK ``` </Step> </Steps> </Accordion> <Accordion title="Delete many records with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X DELETE \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ "https://api.quickblox.com/data/MyScoreTable/5d87d328a28f9a545f1cb558,51c9ab92535c12951b0032dd,5d87d354a28f9a59df1cb557.json" ``` </Step> <Step title="2. Set records IDs"> Set IDs of records that should be deleted via comma. ```curl {3} theme={null} curl -X DELETE \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ "https://api.quickblox.com/data/MyScoreTable/5d87d328a28f9a545f1cb558,51c9ab92535c12951b0032dd,5d87d354a28f9a59df1cb557.json" ``` </Step> <Step title="3. As a result, the API returns a '200 OK' status code which implies that records were deleted."> ```curl theme={null} 200 OK ``` </Step> </Steps> </Accordion> <Accordion title="Delete one record with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X DELETE \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ "https://api.quickblox.com/data/ScoreTable/5d87d2f4a28f9a48631cb55b.json" ``` </Step> <Step title="2. Set a record ID"> Set the ID of the record that should be deleted. ```curl {3} theme={null} curl -X DELETE \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ "https://api.quickblox.com/data/ScoreTable/5d87d2f4a28f9a48631cb55b.json" ``` </Step> <Step title="3. As a result, the API returns a '200 OK' status code which implies that a record was deleted."> ```curl theme={null} 200 OK ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> <ParamField type="string"> ID of the custom object record. Generated automatically by the server after record creation. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "QB-Token: d9c91a69e552145f6fe24dbb40b50ac575012bba" \ https://api.quickblox.com/data/ScoreTable/5d87d2f4a28f9a48631cb55b.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} {} ``` </ResponseExample> # Delete Records by Criteria Source: https://docs.quickblox.com/reference/delete-records-by-criteria DELETE https://api.quickblox.com/data/{class_name}/by_criteria.json You can delete records by criteria. #### Recipes <AccordionGroup> <Accordion title="Delete records by criteria with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X DELETE \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d 'score_value[gt]=5000&expert_mode=true' \ https://api.quickblox.com/data/ScoreTable/by_criteria.json ``` </Step> <Step title="2. As a result, the API returns the number of deleted records."> ```json theme={null} { "total_deleted": 2 } ``` </Step> </Steps> </Accordion> </AccordionGroup> You can use all search operators listed below the request. **Search operators** The request can contain all, some or none of the next search operators: | Operator | Description | | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | lt | **Less Than** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />score\_value\[lt]=1000 | | lte | **Less Than or Equal to** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />score\_value\[lte]=850 | | gt | **Greater Than** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />bonus\_count\[gt]=2.45 | | gte | **Greater Than or Equal to** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />bonus\_count\[gte]=56.443 | | ne | **Not Equal to** operator <br /> <br />Types: integer, float, string, boolean <br /> <br />Example: <br />game\_mode\_name\[ne]=ctf | | in | **Contained IN array** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />game\_mode\_name\[in]=deathmatch,rage | | nin | **Not contained IN array** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />game\_mode\_name\[nin]=survivor,crazy\_nightmare | | all | **ALL contained IN array** operator <br /> <br />Types: array <br /> <br />Example: <br />game\_modes\[all]=survivor,crazy | | or | **OR** operator <br /> <br /> <br />Types: integer, float, string <br /> <br />Example: <br />name\[or]=sam,tim <br />name\[or]=sam\&lastname\[or]=johnson <br /> <br />Will return records with name `sam` **or** `tim`. <br />Will return records with name `sam` **or** last name `johnson`. | | ctn | **Contains** substring operator <br /> <br />Types: string <br /> <br />Example: <br />username\[ctn]=son <br /> <br />Will return all records where `username` field **contains** `son` substring. | | near | Types: location <br /> <br />Example: <br />mylocation\[near]=25.32,44.551;1000 <br /> <br />Search records in a specific radius with the current position in meters. Format: `{field_name}[near]=longitude,latitude;radius`. | #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> #### Body Parameters <ParamField type="string"> Search records with the field that contains the specified value. </ParamField> <ParamField type="object"> <Expandable title="{custom_field_M} object"> <ParamField type="string"> Search a record with the field that contains a value according to the specified value and operator. </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="total_deleted" type="integer" /> </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "QB-Token: d9c91a69e552145f6fe24dbb40b50ac575012bba" \ -d 'score_value[gt]=5000&expert_mode=true' \ https://api.quickblox.com/data/ScoreTable/by_criteria.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "total_deleted": 2 } ``` ```json 400 theme={null} {} ``` </ResponseExample> # Delete Session Source: https://docs.quickblox.com/reference/delete-session DELETE https://api.quickblox.com/session.json Deletes a current session (user or application session). #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "QB-Token: 8b75a6c7191285499d890a81df4ee7fe49bc732a" \ https://api.quickblox.com/session.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 401 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` </ResponseExample> # Delete Subscription Source: https://docs.quickblox.com/reference/delete-subscription DELETE https://api.quickblox.com/subscriptions/{subscription_id}.json Remove a subscription by the identifier. #### Path Parameters <ParamField type="integer">ID of the subscription.</ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response. </Accordion> <Accordion title="403"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -i -X DELETE \ -H "QB-Token: 37d3a10a1843ac780cb6f2c683bddad2b0012bba" \ https://api.quickblox.com/subscriptions/56328256.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 403 theme={null} { "code": null, "message": "Your access to the device_subscription has been restricted, because you are not the owner." } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find DeviceSubscription with 'id'=123" } ``` </ResponseExample> # Delete Token with API key Source: https://docs.quickblox.com/reference/delete-token-with-api-key DELETE https://api.quickblox.com/users/{user_id}/tokens/{token} Deletes a user session using an API key <Warning> You can't use the QB token in this request. It will be ignored if you pass the QB-Token header in the request. </Warning> #### Path Parameters <ParamField type="integer"> ID of the user. Generated automatically by the server after user creation. </ParamField> <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> #### Headers <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="token" type="string" /> <ResponseField name="expires_at" type="string" /> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "Authorization: ApiKey cf5709d6013fdb7a6787fbeb8340afed8aec4c69" \ https://api.quickblox.com/users/123/tokens/cf5709d6013fdb7a6787fbeb8340afed8aec4c69 ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "token": "c56f382b402fb482086db956064592d3cd012bba", "expires_at": "2023-09-01T14:55:56Z" } ``` ```json 401 theme={null} { "errors": { "base": [ "Api Key is not found." ] } } ``` ```json 404 theme={null} { "errors": { "base": [ "User is not found." ] } } ``` </ResponseExample> # Delete User by ID Source: https://docs.quickblox.com/reference/delete-user DELETE https://api.quickblox.com/users/{user_id}.json Delete a user by ID. A user can be deleted only by themselves or an account owner. #### Recipes <AccordionGroup> <Accordion title="Delete user by id with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X DELETE \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/36.json ``` </Step> <Step title="2. As a result, the API remove a user"> ```curl theme={null} curl -X DELETE \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/36.json ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="integer"> ID of the user. Generated automatically by the server after user creation.</ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="400"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="id" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "QB-Token: b284ddf44f5369611fe14e0e588f1cfa855116c8" \ https://api.quickblox.com/users/36.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 400 theme={null} { "code": null, "message": "No data was provided" } ``` ```json 403 theme={null} { "errors": { "base": [ "You are not owner of this object" ] } } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find user" } ``` ```json 422 theme={null} { "errors": { "id": [ "should be a positive integer" ] } } ``` </ResponseExample> # Delete User by External ID Source: https://docs.quickblox.com/reference/delete-user-by-external-id DELETE https://api.quickblox.com/users/external/{external_user_id}.json Delete a user by External User ID. A user can be deleted only by themselves or an account owner. #### Recipes <AccordionGroup> <Accordion title="Delete user by external id with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X DELETE \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/external/697.json ``` </Step> <Step title="2. As a result, the API remove a user"> ```curl theme={null} curl -X DELETE \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/external/697.json ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="integer"> ID of the user in the external system (<Tooltip>External User ID</Tooltip>). </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="id" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "QB-Token: b284ddf44f5369611fe14e0e588f1cfa855116c8" \ https://api.quickblox.com/users/external/697.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 401 theme={null} { "errors": { "base": [ "You are not allowed to modify this user" ] } } ``` ```json 404 theme={null} null ``` ```json 422 theme={null} { "errors": { "id": [ "should be a positive integer" ] } } ``` </ResponseExample> # Dialog Model Source: https://docs.quickblox.com/reference/dialog-model | Property name | Type | Description | | ------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \_id | string | ID of the dialog. Generated automatically by the server after dialog creation. | | user\_id | int | ID of dialog's owner. | | created\_at | datetime | Date & time when a record was created, filled automatically. | | updated\_at | datetime | Date & time when a record was updated, filled automatically. | | type | int | Type of dialog. Possible values are:`type=1` (PUBLIC\_GROUP)`type=2` (GROUP)`type=3` (PRIVATE) | | name | string | Name of a group chat. Makes sense if `type=1` (PUBLIC\_GROUP) or `type=2` (GROUP). The maximum length for the dialog name is 200 symbols. | | photo | string | Photo of a group chat. Makes sense if `type=1` (PUBLIC\_GROUP) or `type=2` (GROUP). Can contain a link to a file in Content module, Custom Objects module or just a web link. | | xmpp\_room\_jid | string | JID of XMPP room for group chat to connect. Null if `type=3` (PRIVATE). Generated automatically by the server after dialog creation. | | occupants\_ids\[] | array of int | Array of users' IDs - dialog occupants. Empty array when `type=1` (PUBLIC\_GROUP) because public dialogs available for everyone. | | admin\_ids\[] | array of int | Array of users' IDs - dialog admins. Applicable only to `type=1` (PUBLIC\_GROUP) and `type=2` (GROUP). For `type=3` (PRIVATE), returns an empty array. Added in v2.52.0. | | last\_message | string | Last sent message in this dialog. | | last\_message\_date\_sent | timestamp | Timestamp of last sent message in this dialog. | | last\_message\_user\_id | int | ID of the user who sent last message in this dialog. | | unread\_messages\_count | int | Number of unread messages in this dialog for a current user. | | is\_join\_required | int (0 or 1) | Defines whether a user must join the dialog before sending/receiving messages via XMPP. Applies only to `type=2` (GROUP). `0` - join not required. `1` - join required. Null if `type=1` or `type=3`. | | data | object | - Information about class and fields in Custom Objects. - Any dialog can be extended using Custom Objects to store additional parameters. | | data.class\_name | string | Class name in Custom Objects. | | data.\{field\_name\_N} | string | Field name of class in Custom Objects. Can be many: 1..N. | # Download File Source: https://docs.quickblox.com/reference/download-file GET https://api.quickblox.com/data/{class_name}/{custom_object_record_id}/file.json Download the content of file fields. #### Recipes <AccordionGroup> <Accordion title="Download file with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d "field_name=avatar" \ https://api.quickblox.com/data/UserProfileClass/5d863a78a0eb474dae6099c6/file.json ``` </Step> <Step title="2. As a result, the API returns the file content."> ```text theme={null} HTTP/1.1 200 OK Content-Type: image/jpeg <binary file content> ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> <ParamField type="string"> ID of the custom object record. Generated automatically by the server after record creation. </ParamField> #### Body Parameters <ParamField type="string"> Custom object record field which contains a file. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: a04ed6adfdb98cde99837088830349f67d012bba" \ -d "field_name=avatar" \ https://api.quickblox.com/data/UserProfileClass/5d863a78a0eb474dae6099c6/file.json ``` </RequestExample> <ResponseExample> ```json 403 theme={null} { "errors": [ "Wrong permission" ] } ``` ```json 404 theme={null} { "errors": [ "Wrong arguments" ] } ``` </ResponseExample> # Download File by UID Source: https://docs.quickblox.com/reference/download-file-by-uid GET https://api.quickblox.com/blobs/{blob_uid}/download.json Download File (get file as a redirect to the S3 object) by UID. <Warning> Declare a file as uploaded first by setting its status to Complete to have a possibility to download the file. </Warning> <Info> If a file is public then it's possible to download it without a session token. </Info> #### Path Parameters <ParamField type="integer">File/blob identifier that should be taken from the response of the Create File request.</ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> #### Responses <AccordionGroup> <Accordion title="301"> A successful response </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ https://api.quickblox.com/blobs/9dc0d37c9be34c839ae3bcac4bbaace100/download.json ``` </RequestExample> <ResponseExample> ```html 301 theme={null} <html> <body>You are being <a href="https://s3.amazonaws.com/qbprod/9dc0d37c9be34c839ae3bcac4bbaace100?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIY7KFM23XGXJ7R7A%2F20190915%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190915T202045Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=2193b5418bc949f8ab5ac26d24a43ecc705ffbebb740689dfe304c636b9516e6">redirected</a> </body> </html> ``` ```json 400 theme={null} {} ``` </ResponseExample> # Errors Source: https://docs.quickblox.com/reference/errors Possible API errors are as follows: | Code | Text | Description | | ---------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [400](https://en.wikipedia.org/wiki/Http_status_codes#400) | Bad Request | Missing or invalid parameter. Possible causes: - malformed request parameters. | | [401](https://en.wikipedia.org/wiki/Http_status_codes#401) | Unauthorized | Authorization is missing or incorrect. Possible causes: - a user tries to authorize with a wrong login or password. - a user uses invalid session token. | | [403](https://en.wikipedia.org/wiki/Http_status_codes#403) | Forbidden | Access has been refused. Possible causes: - a user tries to retrieve chat messages for a chat dialog while they are not in the occupants list. | | [404](https://en.wikipedia.org/wiki/Http_status_codes#404) | Not Found | The requested resource could not be found. Possible causes: - a user tries to retrieve chat messages for the invalid chat dialog ID. - a user tries to retrieve a custom object record with invalid ID. | | [422](https://en.wikipedia.org/wiki/Http_status_codes#422) | Unprocessable Entity | The request was well-formed but was unable to be followed due to validation errors. Possible causes: - create a user with the existent login or email. - provide values in the wrong format to create some object. | | [429](https://en.wikipedia.org/wiki/Http_status_codes#429) | Too Many Requests | Rate limit for your [current plan](https://quickblox.com/pricing/) is exceeded. | | [500](https://en.wikipedia.org/wiki/Http_status_codes#500) | Internal Server Error | Server encountered an error, try again later. | | [503](https://en.wikipedia.org/wiki/Http_status_codes#503) | Service Unavailable | Server is at capacity, try again later. | # Event Model Source: https://docs.quickblox.com/reference/events | Property name | Type | Description | | -------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | int | ID of the event. Generated automatically by the server after event creation. | | notification\_channel | nested object | Declare which notification channels could be used to notify user about events. | | notification\_channel.name | string | Notification channel name. Possible values are: `apns`, `apns_voip`, `gcm`, and `email`. | | event\_type | string | Event type. Possible values are: <br />- `one_shot` - a one-time event caused by an external object (valid only if the `date` is **not** specified). <br />- `fixed_date` - a one-time event that occurs at a specified date (valid only if the `date` is specified). <br />- `period_date` - is a reusable event that occurs within a given period from the initial date (valid only if the `period` is specified). <br /> <br />Default: <br />- `one_shot` - if `date` is **not** specified. <br />- `fixed_date` - if `date` is specified. <br />- `period_date` - if `period` is specified. | | message | string | Encoded message payload. Possible values are: Base64 encoded plain text or push notification payload ([Push notifications formats](/reference/push-notification-formats)). | | date | int | The date when event should be sent. <br />If `event_type=fixed_date`, value of `date` can **not** be in the past. | | period | int | A period of the event in seconds. | | name | string | A name of the event. Service information. Only for your own usage. | | occured\_count | int | A number of times the event was sent. | | created\_at | datetime | Date & time when a record was created, filled automatically. | | updated\_at | datetime | Date & time when a record was updated, filled automatically. | | end\_date | int | A date when the event was completed. Must be **not** less than the `date`. | | active | boolean | Marks the event as active/inactive. | | application\_id | int | ID of the application. | | user\_id | int | ID of the user who created the event. | | kind | string | Kind of the event. <br />Possible values are: `API`, `DashBoard`, and `ChatAlert`. | | environment | string | An environment of the notification. <br /> <br />Possible values are: `development` and `production`. | | tag\_query | string | Select event recipients by tags. | # File Model Source: https://docs.quickblox.com/reference/file-model A **file** field type is created to easily work with the content from Custom Objects module. There is an ability to upload, download, update, and delete the content of file fields. | Property name | Type | Description | | ------------- | ------ | ------------------------------------------------------ | | file\_id | string | ID of the file. | | size | int | Size of the file in bytes. The max file size is 32 MB. | | name | string | File name. | | content\_type | string | <Tooltip> MIME content type</Tooltip> | # Flush Tokens with API key Source: https://docs.quickblox.com/reference/flush-tokens-with-api-key DELETE https://api.quickblox.com/users/{user_id}/tokens Flushes all user-specific API tokens <Warning> You can't use the QB token in this request. It will be ignored if you pass the QB-Token header in the request. </Warning> #### Path Parameters <ParamField type="integer"> ID of the user. Generated automatically by the server after user creation. </ParamField> #### Headers <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "Authorization: ApiKey cf5709d6013fdb7a6787fbeb8340afed8aec4c69" \ https://api.quickblox.com/users/123/tokens ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 401 theme={null} // Invalid authorization header { "errors": { "base": [ "Authorization header is invalid." ] } } // Invalid API key { "errors": { "base": [ "Api Key is not found." ] } } ``` ```json 404 theme={null} { "errors": { "base": [ "User is not found." ] } } ``` </ResponseExample> # Get Account Settings Source: https://docs.quickblox.com/reference/get-account-settings GET https://api.quickblox.com/account_settings.json Retrieve correct API endpoints for account. <Info> Use these endpoints in the application. This should be ideally the first request in the application before all other requests to QuickBlox. </Info> QuickBlox provides a flexible mechanism for apps to retrieve all correct endpoints (`apiEndpoint` , `chatEndpoint` etc.) to work with. This mechanism allows for a smooth transition between [plans](https://quickblox.com/pricing/). We recommend to call it hourly. #### Headers <ParamField type="string"> Provides access to account information. To get the `QB-Account-Key`, **open Dashboard** => **Client section** => **Privacy and Settings** and copy the Account key value. </ParamField> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Account-Key: 7yvNe17TnjNUqDoPwfqp" \ https://api.quickblox.com/account_settings.json ``` </RequestExample> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="api_endpoint" type="string" /> <ResponseField name="chat_endpoint" type="string" /> <ResponseField name="turnserver_endpoint" type="string" /> </Accordion> <Accordion title="404"> An error response </Accordion> </AccordionGroup> <ResponseExample> ```json 200 theme={null} { "api_endpoint": "https://api.quickblox.com", "chat_endpoint": "chat.quickblox.com", "turnserver_endpoint": "turnserver.quickblox.com" } ``` ```json 404 theme={null} {} ``` </ResponseExample> # Get Address Book Source: https://docs.quickblox.com/reference/get-address-book GET https://api.quickblox.com/address_book.json Retrieve all uploaded address book contacts. #### Recipes <AccordionGroup> <Accordion title="Get address book contacts from the device"> <Steps> <Step title="1. Set a device identifier"> Set a `udid` parameter to specify a user's device. ```curl {5} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "udid": "A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="2. As a result, the API returns a list of address book contacts for a specified device."> ```curl theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "udid": "A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Query Parameters <ParamField type="string"> User's device identifier. If specified, all operations will be in this context. Max. length is 64 symbols. If the device identifier is not specified, it means that a user has one global address book across all their devices. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <Expandable title="array of objects"> <ResponseField name="name" type="string" /> <ResponseField name="phone" type="string" /> </Expandable> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 2c7b29abe6af6322bb6bbe1bf6b530c4d1012bba" \ https://api.quickblox.com/address_book.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} [ { "name": "Mike Howard", "phone": "463571393241" }, { "name": "Morgan Fuller", "phone": "65021272571" } ] ``` ```json 404 theme={null} { "errors": [ "Empty address book" ] } ``` </ResponseExample> # Get Dialog by ID Source: https://docs.quickblox.com/reference/get-dialog-by-id GET https://api.quickblox.com/chat/Dialog/{chat_dialog_id}.json Retrieve a dialog by ID. <br/> *Added in v2.39.0* #### Path Parameters <ParamField type="string">ID of the dialog to which a current message is connected. Generated automatically by the server after message creation.</ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="_id" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="last_message" type="string" /> <ResponseField name="last_message_date_sent" type="integer" /> <ResponseField name="last_message_id" type="string" /> <ResponseField name="last_message_user_id" type="integer" /> <ResponseField name="name" type="string" /> <ResponseField name="occupants_ids" type="array of integers" /> <ResponseField name="admin_ids" type="array of integers (added in v2.52.0)" /> <ResponseField name="photo" type="string" /> <ResponseField name="type" type="integer" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="xmpp_room_jid" type="string" /> <ResponseField name="unread_messages_count" type="integer" /> <ResponseField name="is_join_required" type="integer" /> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "_id": "5d75393ba28f9a17e1cb0f9e", "created_at": "2019-09-08T17:24:11Z", "last_message": "sure, I can wait", "last_message_date_sent": 1567971414, "last_message_id": "5d755856a0eb47411e0b72a9", "last_message_user_id": 96753878, "name": "New Year 2020 party", "occupants_ids": [ 96753878, 96754523, 96830631, 96831328, 96831397 ], "admin_ids": [], "photo": "68123", "type": 2, "updated_at": "2019-09-08T19:36:54Z", "user_id": 96753878, "xmpp_room_jid": "76730_5d75393ba28f9a17e1cb0f9e@muc.chat.quickblox.com", "unread_messages_count": 0, "is_join_required": 1 } ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to perform this operation" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # Get Event by ID Source: https://docs.quickblox.com/reference/get-event-by-id GET https://api.quickblox.com/events/{event_id}.json Retrieve event by the ID. The event specified in the request should belong to the application for which the authorization token has been received. #### Path Parameters <ParamField type="integer">ID of the event. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="event" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="event_type" type="string" /> <ResponseField name="message" type="string" /> <ResponseField name="date" type="string" /> <ResponseField name="period" type="string" /> <ResponseField name="name" type="string" /> <ResponseField name="occured_count" type="integer" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="end_date" type="string" /> <ResponseField name="active" type="boolean" /> <ResponseField name="application_id" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="kind" type="string" /> <ResponseField name="environment" type="string" /> <ResponseField name="tag_query" type="string" /> <ResponseField name="notification_channel" type="object"> <Expandable title="properties"> <ResponseField name="name" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: ff40ff1c9964fe8d16042b3c809a21b916012bba" \ https://api.quickblox.com/events/30529342.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "event": { "id": 30529342, "event_type": "period_date", "message": "payload=eyJhcHMiOnsiYWxlcnQiOiJQdXNoIG5vdGlmaWNhdGlvbiBmcm9tIFRheWxvciIsInNvdW5kIjoiZGVmYXVsdCJ9fQ==", "date": 1568359375, "period": 86400, "name": null, "occured_count": 0, "created_at": "2019-09-01T20:19:08Z", "updated_at": "2019-09-01T20:19:08Z", "end_date": 1569359375, "active": true, "application_id": 76730, "user_id": 91141808, "kind": "API", "environment": "production", "tag_query": null, "notification_channel": { "name": "apns" } } } ``` ```json 400 theme={null} {} ``` </ResponseExample> # Get File by ID Source: https://docs.quickblox.com/reference/get-file-by-id GET https://api.quickblox.com/blobs/{blob_id}.json Get information about a file by ID. #### Path Parameters <ParamField type="integer"> ID of the file/blob. Generated automatically by the server after file/blob creation (for example, user photo). </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="blob" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="uid" type="string" /> <ResponseField name="content_type" type="string" /> <ResponseField name="name" type="string" /> <ResponseField name="size" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="blob_status" type="string" /> <ResponseField name="set_completed_at" type="string" /> <ResponseField name="public" type="boolean" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ https://api.quickblox.com/blobs/9715867.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "blob": { "id": 9715867, "uid": "9dc0d37c9be34c839ae3bcac4bbaace100", "content_type": "image/jpeg", "name": "museum.jpeg", "size": 16766, "created_at": "2019-09-15T19:40:18Z", "updated_at": "2019-09-15T20:00:46Z", "blob_status": "complete", "set_completed_at": "2019-09-15T20:00:46Z", "public": false } } ``` ```json 404 theme={null} Empty body ``` </ResponseExample> # Get Message by ID Source: https://docs.quickblox.com/reference/get-message-by-id GET https://api.quickblox.com/chat/Message/{chat_message_id} Retrieve a message by ID. <br/> *Added in v2.39.0* #### Recipes <AccordionGroup> <Accordion title="Get message by ID"> <Steps> <Step title="1. Specify the message ID"> Provide the `message_id` of the message whose reactions you want to retrieve. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98 ``` </Step> <Step title="2. As a result, the API returns a specified message."> ```curl theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98 ``` </Step> </Steps> </Accordion> <Accordion title="Get message with reactions (added in v2.58.0)"> <Steps> <Step title="1. Specify the message ID"> Provide the `message_id` of the message whose reactions you want to retrieve. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98?include_reactions=1 ``` </Step> <Step title="2. Set the `include_reactions` query parameter"> To include detailed reactions data in the response, set the `include_reactions=1` query parameter. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98?include_reactions=1 ``` </Step> <Step title="3. As a result, the API returns a specified message with reactions."> ```curl theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98?include_reactions=1 ``` </Step> </Steps> </Accordion> <Accordion title="Get message by ID with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98 ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98 ``` </Step> <Step title="3. Specify the message ID"> Provide the `message_id` of the message whose reactions you want to retrieve. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98 ``` </Step> <Step title="4. As a result, the API returns a specified message."> ```curl theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98 ``` </Step> </Steps> </Accordion> <Accordion title="Get message with reactions using API key (added in v2.58.0)"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98?include_reactions=1 ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98?include_reactions=1 ``` </Step> <Step title="3. Specify the message ID"> Provide the `message_id` of the message whose reactions you want to retrieve. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98?include_reactions=1 ``` </Step> <Step title="4. Set the `include_reactions` query parameter"> To include detailed reactions data in the response, set the `include_reactions=1` query parameter. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98?include_reactions=1 ``` </Step> <Step title="3. As a result, the API returns a specified message with reactions."> ```curl theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98?include_reactions=1 ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> ID of the message. Generated automatically by the server after message creation. </ParamField> #### Query Parameters <ParamField type="integer"> Optional. Set to `1` to include reactions data in the message response. Default is `0`. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="message" type="string" /> <ResponseField name="attachments" type="array"> <Expandable title="properties"> <ResponseField name="type" type="string" /> <ResponseField name="id" type="string" /> </Expandable> </ResponseField> <ResponseField name="_id" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="chat_dialog_id" type="string" /> <ResponseField name="date_sent" type="integer" /> <ResponseField name="sender_id" type="integer" /> <ResponseField name="recipient_id" type="integer" /> <ResponseField name="read_ids" type="array" /> <ResponseField name="delivered_ids" type="array" /> <ResponseField name="read" type="integer" /> <ResponseField name="reactions" type="array of objects"> List of reactions added to the message. *This field is **omitted by default**. To include it, use the query parameter `?include_reactions=1`.* <Expandable title="properties"> <ResponseField name="name" type="string">Type of the reaction, e.g. "like", "love".</ResponseField> <ResponseField name="count" type="integer">Number of times this reaction was added.</ResponseField> <ResponseField name="user_ids" type="array of integers">IDs of users who added this reaction.</ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ https://api.quickblox.com/chat/Message/66f2a030bba14b4c2e000000 ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "message": "hello", "attachments": [], "_id": "66f2a030bba14b4c2e000000", "created_at": "2024-09-24T11:19:12Z", "updated_at": "2024-09-24T11:19:12Z", "chat_dialog_id": "66ed332108a4fb0d2dc2d147", "date_sent": 1727176752, "sender_id": 20618, "recipient_id": 0, "read_ids": [ 20618 ], "delivered_ids": [ 20618 ], "read": 0 } ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to perform this operation" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # Get Notification Settings Source: https://docs.quickblox.com/reference/get-notification-settings GET https://api.quickblox.com/chat/Dialog/{chat_dialog_id}/notifications.json Get status if push notifications are turned on/off for offline messages in a dialog. #### Recipes <AccordionGroup> <Accordion title="Get Notification Settings with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </Step> <Step title="3. Set dialog ID"> Specify the ID of the dialog whose setting status you want to get. ```curl {5} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </Step> <Step title="4. As result, the API returns the notification settings status"> ```curl theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> ID of the dialog to which a current message is connected. Generated automatically by the server after message creation</ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="notifications" type="object"> <Expandable title="properties"> <ResponseField name="enabled" type="integer" /> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "notifications": { "enabled": 0 } } ``` ```json 400 theme={null} {} ``` </ResponseExample> # List Registered Users Source: https://docs.quickblox.com/reference/get-registered-contacts-users GET https://api.quickblox.com/address_book/registered_users.json Retrieve users who have already registered from your address book. #### Recipes <AccordionGroup> <Accordion title="Retrieve compact list of registered users"> <Steps> <Step title="1. Set a device identifier"> Set a `udid` parameter to specify a user's device. If specified, contacts will be returned for a specified device only. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 69049dfb31898c7b2f3fd7016bb48ecd40012bba" \ https://api.quickblox.com/address_book/registered_users.json?udid=A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB&compact=1 ``` </Step> <Step title="2. Specify if you want to get a compact list with contacts"> Set a `compact` parameter as `1` to get a compact list of contacts. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 69049dfb31898c7b2f3fd7016bb48ecd40012bba" \ https://api.quickblox.com/address_book/registered_users.json?udid=A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB&compact=1 ``` </Step> <Step title="3. As a result, the API returns a list of users' IDs and phone numbers."> ```curl theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 69049dfb31898c7b2f3fd7016bb48ecd40012bba" \ https://api.quickblox.com/address_book/registered_users.json?udid=A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB&compact=1 ``` </Step> </Steps> </Accordion> </AccordionGroup> Users are matched with address book contacts by phone number, so user and address book contact must have the same phone number to be included in response. #### Query Parameters <ParamField type="string"> User's device identifier. If specified, all operations will be in this context. Max. length is 64 symbols. If the device identifier is not specified, it means that a user has one global address book across all their devices. </ParamField> <ParamField type="integer"> Defines force rewrite mode. Set `force=1` to apply, then all previous contacts for the device context will be replaced by new ones. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <Expandable title="compact=0"> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Expandable> <Expandable title="compact=1"> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="phone" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Expandable> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 69049dfb31898c7b2f3fd7016bb48ecd40012bba" \ https://api.quickblox.com/address_book/registered_users.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} // compact=0 { "items": [ { "user": { "id": 96830631, "full_name": "Mike Howard", "email": "mike@gmail.com", "login": "Mike", "phone": "463571393241", "website": "https://mysite.com", "created_at": "2019-08-29T18:58:22Z", "updated_at": "2020-01-27T14:27:12Z", "last_request_at": "2019-09-09T21:01:58Z", "external_user_id": 172, "facebook_id": "8866", "twitter_id": "9366", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "IOS,web" } } ] } // compact=1 { "items": [ { "user": { "id": 96830631, "phone": "463571393241" } }, { "user": { "id": 96831328, "phone": "65021272571" } } ] } ``` ```json 404 theme={null} { "errors": [ "Empty address book" ] } ``` </ResponseExample> # Get Session Source: https://docs.quickblox.com/reference/get-session GET https://api.quickblox.com/session.json Gets information about a current session. #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="session" type="object"> <Expandable title="Properties"> <ResponseField name="application_id" type="integer" /> <ResponseField name="created_at" type="string" /> <ResponseField name="id" type="integer" /> <ResponseField name="nonce" type="integer" /> <ResponseField name="token" type="string" /> <ResponseField name="ts" type="integer" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="_id" type="string" /> </Expandable> </ResponseField> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: 8b75a6c7191285499d890a81df4ee7fe49bc732a" \ https://api.quickblox.com/session.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "session": { "application_id": 76730, "created_at": "2019-08-26T20:09:22+00:00", "id": 1148462029, "nonce": 4214, "token": "8b75a6c7191285499d890a81df4ee7fe49bc732a", "ts": 1566850162, "updated_at": "2019-08-26T20:09:22+00:00", "user_id": 91105670, "_id": "1148462029" } } ``` ```json 401 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` </ResponseExample> # Get Unread Messages Count Source: https://docs.quickblox.com/reference/get-unread-message-count GET https://api.quickblox.com/chat/Message/unread.json Retrieve an unread count of your chat messages, split across dialogs or just a total value. #### Recipes <AccordionGroup> <Accordion title="Display total number of unread messages for each dialog"> <Steps> <Step title="1. Set dialogs IDs."> Specify as many dialogs IDs as you need via comma. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d "chat_dialog_ids=5d753421a28f9a2596cb0f98,5d75393ba28f9a17e1cb0f9e,5d75327ba28f9a0f32cb0f99,5d7539bca28f9a2cb3cb0f99" \ https://api.quickblox.com/chat/Message/unread.json ``` </Step> <Step title="2. As a result, the API returns a number of unread messages for each specified dialog."> ```curl theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d "chat_dialog_ids=5d753421a28f9a2596cb0f98,5d75393ba28f9a17e1cb0f9e,5d75327ba28f9a0f32cb0f99,5d7539bca28f9a2cb3cb0f99" \ https://api.quickblox.com/chat/Message/unread.json ``` </Step> </Steps> </Accordion> <Accordion title="Display total number of unread messages for each dialog with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d "chat_dialog_ids=5d753421a28f9a2596cb0f98,5d75393ba28f9a17e1cb0f9e,5d75327ba28f9a0f32cb0f99,5d7539bca28f9a2cb3cb0f99" \ https://api.quickblox.com/chat/Message/unread.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d "chat_dialog_ids=5d753421a28f9a2596cb0f98,5d75393ba28f9a17e1cb0f9e,5d75327ba28f9a0f32cb0f99,5d7539bca28f9a2cb3cb0f99" \ https://api.quickblox.com/chat/Message/unread.json ``` </Step> <Step title="3. Set dialogs IDs"> Specify as many dialogs IDs as you need via comma. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d "chat_dialog_ids=5d753421a28f9a2596cb0f98,5d75393ba28f9a17e1cb0f9e,5d75327ba28f9a0f32cb0f99,5d7539bca28f9a2cb3cb0f99" \ https://api.quickblox.com/chat/Message/unread.json ``` </Step> <Step title="4. As a result, the API returns a number of unread messages for each specified dialog."> ```curl theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d "chat_dialog_ids=5d753421a28f9a2596cb0f98,5d75393ba28f9a17e1cb0f9e,5d75327ba28f9a0f32cb0f99,5d7539bca28f9a2cb3cb0f99" \ https://api.quickblox.com/chat/Message/unread.json ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Query Parameters <ParamField type="string">Array of dialogs IDs to get a count of unread messages for. You also can omit this parameter to receive just a total count of all unread messages.</ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="total" type="integer" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ https://api.quickblox.com/chat/Message/unread.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "total": 15 } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # Get User by Email Source: https://docs.quickblox.com/reference/get-user-by-email GET https://api.quickblox.com/users/by_email.json Retrieve a user by email. #### Recipes <AccordionGroup> <Accordion title="Get user by email with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_email.json?email=alex@gmail.com ``` </Step> <Step title="2. As a result, the API returns a user with the specified email in the path params"> ```curl theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_email.json?email=alex@gmail.com ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Query Parameters <ParamField type="string">User's email.</ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: bdbb2bd5abb81b1d7fe7be03db9305b46e012bba" \ https://api.quickblox.com/users/by_email.json?email=alex@gmail.com ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "user": { "id": 96754523, "full_name": "Alex Miller", "email": "alex@gmail.com", "login": "Alex", "phone": "+3123555", "website": "https://mysite.com", "created_at": "2019-08-27T21:08:55Z", "updated_at": "2019-08-27T21:08:55Z", "last_request_at": null, "external_user_id": 126672, "facebook_id": "888766", "twitter_id": "982366", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "android,web" } } ``` ```json 401 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` ```json 403 theme={null} { "errors": { "base": [ "Users retrieving forbidden in admin panel, application settings. Setup it here https://admin.quickblox.com/apps/{app_id}/service/users/settings" ] } } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find user" } ``` </ResponseExample> # Get User by External User ID Source: https://docs.quickblox.com/reference/get-user-by-external-user-id GET https://api.quickblox.com/users/external/{external_user_id}.json Retrieve a user by External User ID. #### Recipes <AccordionGroup> <Accordion title="Get user by external user id with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/external/1236672.json ``` </Step> <Step title="2. As a result, the API returns a user with the specified external user id"> ```curl theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/external/1236672.json ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Query Parameters <ParamField type="integer"> ID of the user in the external system <Tooltip>External User ID</Tooltip>. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="id" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: bdbb2bd5abb81b1d7fe7be03db9305b46e012bba" \ https://api.quickblox.com/users/external/133712.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "user": { "id": 96831397, "full_name": "Jen Lesser", "email": "jen@gmail.com", "login": "Jen", "phone": "+1331234215", "website": "https://mysite.com", "created_at": "2019-08-29T19:15:17Z", "updated_at": "2019-08-29T19:15:17Z", "last_request_at": null, "external_user_id": 133712, "facebook_id": "1338866", "twitter_id": "1339366", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "android" } } ``` ```json 401 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` ```json 403 theme={null} { "errors": { "base": [ "Users retrieving forbidden in admin panel, application settings. Setup it here https://admin.quickblox.com/apps/{app_id}/service/users/settings" ] } } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find user" } ``` ```json 422 theme={null} { "errors": { "id": [ "should be a positive integer" ] } } ``` </ResponseExample> # Get User by Facebook ID Source: https://docs.quickblox.com/reference/get-user-by-facebook GET https://api.quickblox.com/users/by_facebook_id.json Search a user by Facebook identifier. #### Recipes <AccordionGroup> <Accordion title="Get user by facebook id with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_facebook_id.json?facebook_id=888766 ``` </Step> <Step title="2. As a result, the API returns a user with the specified facebook id in the path params."> ```curl theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_facebook_id.json?facebook_id=888766 ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Query Parameters <ParamField type="string">ID of the user's Facebook account.</ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: a1e6b25b27ab3771a391b5c1173b572aed012bba" \ https://api.quickblox.com/users/by_facebook_id.json?facebook_id=888766 ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "user": { "id": 96754523, "full_name": "Alex Miller", "email": "alex@gmail.com", "login": "Alex", "phone": "+3123555", "website": "https://mysite.com", "created_at": "2019-08-27T21:08:55Z", "updated_at": "2019-08-27T21:08:55Z", "last_request_at": null, "external_user_id": 126672, "facebook_id": "888766", "twitter_id": "982366", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "android,web" } } ``` ```json 401 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` ```json 403 theme={null} { "errors": { "base": [ "Users retrieving forbidden in admin panel, application settings. Setup it here https://admin.quickblox.com/apps/{app_id}/service/users/settings" ] } } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find user" } ``` </ResponseExample> # Get User by ID Source: https://docs.quickblox.com/reference/get-user-by-id GET https://api.quickblox.com/users/{user_id}.json Retrieve a user by ID. #### Recipes <AccordionGroup> <Accordion title="Get user by id with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/96752798.json ``` </Step> <Step title="2. As a result, the API returns a user with the specified id in the path params."> ```curl theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/96752798.json ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="integer"> ID of the user. Generated automatically by the server after user creation.</ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="id" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: 6f8c8931e80d0d828341ef218e95584ac48195db" \ https://api.quickblox.com/users/96752798.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "user": { "id": 96752798, "full_name": "Peter Smith", "email": "peter@gmail.com", "login": "Peter", "phone": "+73123555", "website": "https://mysite.com", "created_at": "2019-08-27T20:19:08Z", "updated_at": "2019-08-27T20:19:08Z", "last_request_at": "2019-08-27T20:49:21Z", "external_user_id": 1236672, "facebook_id": "8887366", "twitter_id": "9982366", "blob_id": null, "custom_data": "lead of marketing team", "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "marketing,seo" } } ``` ```json 401 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` ```json 403 theme={null} { "errors": { "base": [ "Forbidden" ] } } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find user" } ``` ```json 422 theme={null} { "errors": { "id": [ "should be a positive integer" ] } } ``` </ResponseExample> # Get User by Login Source: https://docs.quickblox.com/reference/get-user-by-login GET https://api.quickblox.com/users/by_login.json Search a user by login. #### Recipes <AccordionGroup> <Accordion title="Get user by login with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_login.json?login=Peter ``` </Step> <Step title="2. As a result, the API returns a user with the specified login in the path params."> ```curl theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_login.json?login=Peter ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Query Parameters <ParamField type="integer">User's login.</ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: a1e6b25b27ab3771a391b5c1173b572aed012bba" \ https://api.quickblox.com/users/by_login.json?login=Alex ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "user": { "id": 96754523, "full_name": "Alex Miller", "email": "alex@gmail.com", "login": "Alex", "phone": "+3123555", "website": "https://mysite.com", "created_at": "2019-08-27T21:08:55Z", "updated_at": "2019-08-27T21:08:55Z", "last_request_at": null, "external_user_id": 126672, "facebook_id": "888766", "twitter_id": "982366", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "android,web" } } ``` ```json 401 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` ```json 403 theme={null} { "errors": { "base": [ "Users retrieving forbidden in admin panel, application settings. Setup it here https://admin.quickblox.com/apps/{app_id}/service/users/settings" ] } } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find user" } ``` </ResponseExample> # Limits Source: https://docs.quickblox.com/reference/limits The purpose of the **rate limit** is to ensure a high quality of QuickBlox service across all QuickBlox accounts, by limiting the number of API requests that an account can produce per second. If you go over these limits when using REST based APIs, QuickBlox will start returning a **HTTP 429 Too Many Requests** error. Read more info about limits on [Plans page](https://quickblox.com/pricing/). | Your membership plan | API requests per second (per account) | | -------------------- | ------------------------------------- | | Basic | 80 | | Startup | 120 | | Growth | 160 | # List Dialogs Source: https://docs.quickblox.com/reference/list-dialogs GET https://api.quickblox.com/chat/Dialog.json Retrieve a list of chat dialogs. You can list dialogs using specific search criteria. #### Recipes <AccordionGroup> <Accordion title="Filter dialogs by custom field"> <Steps> <Step title="1. Set a custom object class"> You need to refer to the specific custom object class in the request. In this case, we refer to the `CoolDialog` custom object class. See [this section](/reference/create-class) to learn how to create a custom object class. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ https://api.quickblox.com/chat/Dialog.json?data[class_name]=CoolDialog&data[category]=friends ``` </Step> <Step title="2. Set a custom object field"> Refer to a specific custom object field in the query. Here, we refer to the `friends` field of the `CoolDialog` custom object class. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ https://api.quickblox.com/chat/Dialog.json?data[class_name]=CoolDialog&data[category]=friends ``` </Step> <Step title="3. As a result, the API returns all dialogs by the specified custom object field."> ```curl theme={null} curl -X GET \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ https://api.quickblox.com/chat/Dialog.json?data[class_name]=CoolDialog&data[category]=friends ``` </Step> </Steps> </Accordion> <Accordion title="Display total number of all dialogs"> <Steps> <Step title="1. Specify if you want to count dialogs"> You can display the total number of all dialogs in the system by setting the `count` to `1`. ```curl {3} theme={null} curl -X GET\ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ https://api.quickblox.com/chat/Dialog.json?count=1 ``` </Step> <Step title="2. As a result, the API returns a total number of all dialogs that exist in the system."> ```curl theme={null} curl -X GET\ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ https://api.quickblox.com/chat/Dialog.json?count=1 ``` </Step> </Steps> </Accordion> <Accordion title="Get dialog by dialog ID"> <Steps> <Step title="1. Set the dialog ID"> You can get a dialog by its ID. Just specify the ID of the dialog in the request. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d "_id=5d75393ba28f9a17e1cb0f9e" \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. As a result, the specified dialog is returned"> ```curl theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d "_id=5d75393ba28f9a17e1cb0f9e" \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Filter dialogs by type"> <Steps> <Step title="1. Set a search operator for a field"> Use a search operator to perform a query. Here, the `in` operator is applied to a `type` field. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d "type[in]=1,2&sort_desc=last_message_date_sent&limit=3" \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Set a sort operator for a field"> Use a sort operator to order the search results by the field. Here, the `sort_desc` operator is applied to the `last_message_date_sent` field. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d "type[in]=1,2&sort_desc=last_message_date_sent&limit=3" \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. Set a limit for a number of dialogs on the page"> You can limit search results on the page using the `limit` parameter. Here, the number of dialogs on the page is limited to `3`. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d "type[in]=1,2&sort_desc=last_message_date_sent&limit=3" \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="4. As a result, the API returns all public and group dialogs."> The request will return the latest **3** records sorted by the **last\_message\_date\_sent** field in descending order. ```curl theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d "type[in]=1,2&sort_desc=last_message_date_sent&limit=3" \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> <Accordion title="Get dialogs with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog.json ``` </Step> <Step title="3. As a result, the API returns all public and group dialogs."> ```curl theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Dialog.json ``` </Step> </Steps> </Accordion> </AccordionGroup> **Search Operators** The request can contain all, some or none of the next search operators. | Operator | Description | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | lt | **Less Than** operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: last\_message\_date\_sent, created\_at, updated\_at <br /> <br />Example: <br />?updated\_at\[lt]=1556829805 <br /> <br />Will return dialogs with `updated_at` **less than** 1556829805. | | lte | **Less Than or Equal** to operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: last\_message\_date\_sent, created\_at, updated\_at <br /> <br />Example: <br />?last\_message\_date\_sent\[lte]=1556735709 <br /> <br />Will return dialogs with `last_message_date_sent` **less than or equal** 1556735709. | | gt | **Greater Than** operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: last\_message\_date\_sent, created\_at, updated\_at <br /> <br />Example: <br />?last\_message\_date\_sent\[gt]=1556735709 <br /> <br />Will return dialogs with `last_message_date_sent` **greater than** 1556735709. | | gte | **Greater Than or Equal** to operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: last\_message\_date\_sent, created\_at, updated\_at <br /> <br />Example: <br />?last\_message\_date\_sent\[gt]=1556735709 <br /> <br />Will return dialogs with `last_message_date_sent` **greater than** 1556735709. | | ne | **Not Equal** to operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: \_id, name, last\_message\_date\_sent <br /> <br />Example: <br /> <br />?\_id\[ne]=5cc9bb58a28f9a406659d777 <br /> <br />Will return dialogs with `_id` **not equal** to 5cc9bb58a28f9a406659d777. | | in | **Contained IN** array operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: type, last\_message\_date\_sent, name <br /> <br />Example: <br />?type\[in]=1,2 <br /> <br />Will return dialogs with type **equal** to 1 and **equal** to 2. | | nin | **Not contained IN** array operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: last\_message\_date\_sent <br /> <br />Example: <br />?last\_message\_date\_sent\[nin]=1556739654, 1556739987 <br /> <br />Will return dialogs with `last_message_date_sent` **not equal** to 1556739654 and 1556739987. | | all | **ALL contained IN** array <br /> <br />Types: number <br /> <br />Applicable to fields: occupants\_ids, admin\_ids <br /> <br />Example: <br />?occupants\_ids\[all]=91103344,91142495 <br /> <br />Will return dialogs with `occupants_ids` **equal** to 91103344 and 91142495 (both). | | ctn | **Contains** substring operator <br /> <br />Types: string <br /> <br />Applicable to field: name <br /> <br />Example: ?name\[ctn]=party <br /> <br />Returns dialogs whose name **contains** the word "party". | **Sort Operators** Next sorting operators are allowed: | Operator | Description | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sort\_asc | Types: all types <br /> <br />Example: sort\_asc=field\_name <br /> <br />Search results will be sorted by the specified field in ascending order. | | sort\_desc | Types: all types <br /> <br />Example: <br />sort\_desc=field\_name <br /> <br />Search results will be sorted by the specified field in descending order. | #### Query Parameters <ParamField type="string"> Search records with the field that contains the specified value. Can be many 1..N. </ParamField> <ParamField type="string"> Filters records with field that contains the value according to the specified value and operator. Can be many 1..N. </ParamField> <ParamField type="integer"> Unread message count is calculated for dialogs by default. Set to 0 to skip calculation, which will speed up the request.\ *Added in v2.4.5.* </ParamField> <ParamField type="integer"> Limit search results to N records. Useful for pagination. </ParamField> <ParamField type="string"> Skip N records in search results. Useful for pagination. </ParamField> <ParamField type="string"> Count search results. Set to 1 to apply. Response will contain only count of records found. If not passed, the response will contain all available dialog records along with their count. </ParamField> <ParamField type="string"> Search results will be sorted by the specified field in descending order. </ParamField> <ParamField type="string"> Search results will be sorted by the specified field in ascending order. </ParamField> <ParamField type="string"> Class name in Custom Objects. </ParamField> <ParamField type="string">Field name of class in Custom Objects. Can be many 1..N.</ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="total_entries" type="integer" /> <ResponseField name="skip" type="integer" /> <ResponseField name="limit" type="integer" /> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="_id" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="last_message" type="string" /> <ResponseField name="last_message_date_sent" type="integer" /> <ResponseField name="last_message_id" type="string" /> <ResponseField name="last_message_user_id" type="integer" /> <ResponseField name="name" type="string" /> <ResponseField name="occupants_ids" type="array of integers" /> <ResponseField name="admin_ids" type="array of integers (added in v2.52.0)" /> <ResponseField name="photo" type="string" /> <ResponseField name="type" type="integer" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="xmpp_room_jid" type="string" /> <ResponseField name="unread_messages_count" type="integer" /> <ResponseField name="is_join_required" type="integer" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ https://api.quickblox.com/chat/Dialog.json?limit=2 ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "total_entries": 6, "skip": 0, "limit": 2, "items": [ { "_id": "5d75393ba28f9a17e1cb0f9e", "created_at": "2019-09-08T17:24:11Z", "last_message": "sure, I can wait", "last_message_date_sent": 1567971414, "last_message_id": "5d755856a0eb47411e0b72a9", "last_message_user_id": 96753878, "name": "New Year party", "occupants_ids": [ 96753878, 96754523, 96830631, 96831328, 96831397 ], "admin_ids": [], "photo": "68123", "type": 2, "updated_at": "2019-09-08T19:36:54Z", "user_id": 96753878, "xmpp_room_jid": "76730_5d75393ba28f9a17e1cb0f9e@muc.chat.quickblox.com", "unread_messages_count": 0, "is_join_required": 1 }, { "_id": "5d753421a28f9a2596cb0f98", "created_at": "2019-09-08T17:02:26Z", "last_message": "let's do it at 7pm", "last_message_date_sent": 1567971391, "last_message_id": "5d75583fa0eb473c350b72ab", "last_message_user_id": 96753878, "name": "Daniel's birthday", "occupants_ids": [ 96753878, 96754523, 96830631, 96831328, 96831397 ], "admin_ids": [], "photo": "68123", "type": 2, "updated_at": "2019-09-08T19:36:31Z", "user_id": 96753878, "xmpp_room_jid": "76730_5d753421a28f9a2596cb0f98@muc.chat.quickblox.com", "unread_messages_count": 0, "is_join_required": 1 } ] } ``` </ResponseExample> # List Events Source: https://docs.quickblox.com/reference/list-events GET https://api.quickblox.com/events.json Get all events that were created by a user specified in the authorization token. #### Query Parameters <ParamField type="integer"> Page of results to return. </ParamField> <ParamField type="integer"> Number of records to return in one request. Min: 1. Max: 100. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="current_page" type="integer" /> <ResponseField name="per_page" type="integer" /> <ResponseField name="total_entries" type="integer" /> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="event" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="event_type" type="string" /> <ResponseField name="message" type="string" /> <ResponseField name="date" type="string" /> <ResponseField name="period" type="string" /> <ResponseField name="name" type="string" /> <ResponseField name="occured_count" type="integer" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="end_date" type="string" /> <ResponseField name="active" type="boolean" /> <ResponseField name="application_id" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="kind" type="string" /> <ResponseField name="environment" type="string" /> <ResponseField name="tag_query" type="string" /> <ResponseField name="notification_channel" type="object"> <Expandable title="properties"> <ResponseField name="name" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: b9c808a7a9d44ca5804f38c43f62d9d0ef8c8020" \ https://api.quickblox.com/events.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "current_page": 1, "per_page": 10, "total_entries": 3, "items": [ { "event": { "id": 30528610, "event_type": "period_date", "message": "data.message=JTdCJTIyYXBzJTIyJTNBJTdCJTIyc291bmQlMjIlM0ElMjJkZWZhdWx0JTIyJTJDJTIyYWxlcnQlMjIlM0ElMjJZb3UraGF2ZStqdXN0K3JlY2VpdmVkK2ErbmV3K21lc3NhZ2UrZnJvbStZdWxpYStTeWRvcmVua28uJTIyJTdEJTJDJTIyeCUyMiUzQSU3QiUyMmRhdGElMjIlM0ElMjIlN0IlNUMlMjJ0eXBlJTVDJTIyJTNBJTVDJTIybWVzc2FnZSU1QyUyMiUyQyU1QyUyMmZyb21fbmFtZSU1QyUyMiUzQSU1QyUyMll1bGlhK1N5ZG9yZW5rbyU1QyUyMiUyQyU1QyUyMmZyb21faWQlNUMlMjIlM0ElNUMlMjI4NTc1MTElNUMlMjIlMkMlNUMlMjJmcm9tX2ZhY2Vib29rJTVDJTIyJTNBJTVDJTIyMTAwMDAwMzQ5MDgyNjAzJTVDJTIyJTdEJTIyJTdEJTdE", "date": 1568359375, "period": 86400, "name": null, "occured_count": 0, "created_at": "2019-09-01T17:38:56Z", "updated_at": "2019-09-01T17:38:56Z", "end_date": 1569359375, "active": true, "application_id": 76730, "user_id": 91141808, "kind": "API", "environment": "development", "tag_query": null, "notification_channel": { "name": "gcm" } } }, { "event": { "id": 30528641, "event_type": "period_date", "message": "payload=eyJhcHMiOnsiYWxlcnQiOiJ7XCJhcHNcIjp7XCJzb3VuZFwiOlwiZGVmYXVsdFwiLFwiYWxlcnRcIjpcIllvdSBoYXZlIGp1c3QgcmVjZWl2ZWQgYSBuZXcgbWVzc2FnZSBmcm9tIFl1bGlhIFN5ZG9yZW5rby5cIn0sXCJ4XCI6e1wiZGF0YVwiOlwie1xcXCJ0eXBlXFxcIjpcXFwibWVzc2FnZVxcXCIsXFxcImZyb21fbmFtZVxcXCI6XFxcIll1bGlhIFN5ZG9yZW5rb1xcXCIsXFxcImZyb21faWRcXFwiOlxcXCI4NTc1MTFcXFwiLFxcXCJmcm9tX2ZhY2Vib29rXFxcIjpcXFwiMTAwMDAwMzQ5MDgyNjAzXFxcIn1cIn19Iiwic291bmQiOiJkZWZhdWx0In19", "date": 1568359375, "period": 86400, "name": null, "occured_count": 0, "created_at": "2019-09-01T17:47:30Z", "updated_at": "2019-09-01T17:47:30Z", "end_date": 1569359375, "active": true, "application_id": 76730, "user_id": 91141808, "kind": "API", "environment": "production", "tag_query": null, "notification_channel": { "name": "apns" } } }, { "event": { "id": 30529135, "event_type": "one_shot", "message": "data.message=UHVzaCUyMG5vdGlmaWNhdGlvbiUyMGZyb20lMjBhdXRvdGVzdA==", "date": null, "period": null, "name": "test", "occured_count": 1, "created_at": "2019-09-01T19:52:01Z", "updated_at": "2019-09-01T19:52:01Z", "end_date": null, "active": false, "application_id": 76730, "user_id": 91141808, "kind": "API", "environment": "development", "tag_query": null, "notification_channel": { "name": "gcm" } } } ] } ``` ```json 400 theme={null} {} ``` </ResponseExample> # List Files Source: https://docs.quickblox.com/reference/list-files GET https://api.quickblox.com/blobs.json Get a list of files/blobs for a current user. The ID of the user is taken from the token specified in the request. #### Query Parameters <ParamField type="integer"> Page of results to return. </ParamField> <ParamField type="integer"> Number of records to return in one request. Min: 1. Max: 100. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="current_page" type="integer" /> <ResponseField name="per_page" type="integer" /> <ResponseField name="total_entries" type="integer" /> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="blob" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="uid" type="string" /> <ResponseField name="content_type" type="string" /> <ResponseField name="name" type="string" /> <ResponseField name="size" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="blob_status" type="string" /> <ResponseField name="set_completed_at" type="string" /> <ResponseField name="public" type="boolean" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ https://api.quickblox.com/blobs.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "current_page": 1, "per_page": 10, "total_entries": 2, "items": [ { "blob": { "id": 9715927, "uid": "9019061f0e094284a250189edd3034f300", "content_type": "image/jpeg", "name": "apple", "size": 16766, "created_at": "2019-09-15T21:12:56Z", "updated_at": "2019-09-15T21:22:17Z", "blob_status": "complete", "set_completed_at": "2019-09-15T21:17:45Z", "public": false } }, { "blob": { "id": 9717123, "uid": "ff61838dc80f46579d59afef479f0a0200", "content_type": "image/jpeg", "name": "my_cat.jpeg", "size": null, "created_at": "2019-09-16T19:26:45Z", "updated_at": "2019-09-16T19:26:45Z", "blob_status": null, "set_completed_at": null, "public": true } } ] } ``` ```json 400 theme={null} {} ``` </ResponseExample> # List Messages Source: https://docs.quickblox.com/reference/list-messages GET https://api.quickblox.com/chat/Message.json Retrieve all chat messages within a particular dialog. #### Recipes <AccordionGroup> <Accordion title="Filter messages by word"> <Steps> <Step title="1. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&message[ctn]=guys ``` </Step> <Step title="2. Set a search operator for a field"> Apply a search operator to the query. Here, a `ctn` operator is applied to a `message` field with the `guys` value. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&message[ctn]=guys ``` </Step> <Step title="3. As a result, the API will return messages satisfying the specified criteria."> All messages containing the **guys** word are returned for the specified dialog. ```curl theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&message[ctn]=guys ``` </Step> </Steps> </Accordion> <Accordion title="Filter and sort messages"> <Steps> <Step title="1. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> <Step title="2. Set a search operator for a field"> Apply a search operator to the query. Here, the `lt` operator is applied to a `date_sent` field. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> <Step title="3. Set a sort operator for a field"> Use a sort operator to order the search results by the field. Here, the `sort_desc` operator is applied to the `date_sent` field. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> <Step title="4. As a result, the API returns messages filtered and sorted upon the request."> The messages are returned with the **date\_sent** value less than **1568057349** and are sorted by the **date\_sent** field in **descending** order. ```curl theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> </Steps> </Accordion> <Accordion title="Get message by its ID"> <Steps> <Step title="1. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&_id=5d76a618a28f9a1edfcb0f9b ``` </Step> <Step title="2. Set the ID of the message"> Set an `_id` parameter to refer to a particular message. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&_id=5d76a618a28f9a1edfcb0f9b ``` </Step> <Step title="3. As a result, the API returns a specified message."> ```curl theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&_id=5d76a618a28f9a1edfcb0f9b ``` </Step> </Steps> </Accordion> <Accordion title="Get messages with reactions (added in v2.58.0)"> <Steps> <Step title="1. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&include_reactions=1 ``` </Step> <Step title="2. Set the `include_reactions` query parameter"> To include detailed reactions data in the response, set the `include_reactions=1` query parameter. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&include_reactions=1 ``` </Step> <Step title="3. As a result, the API returns messages with reactions."> ```curl theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&include_reactions=1 ``` </Step> </Steps> </Accordion> <Accordion title="Get message by its ID with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&_id=5d76a618a28f9a1edfcb0f9b ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&_id=5d76a618a28f9a1edfcb0f9b ``` </Step> <Step title="3. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&_id=5d76a618a28f9a1edfcb0f9b ``` </Step> <Step title="4. Set the ID of the message"> Set an `_id` parameter to refer to a particular message. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&_id=5d76a618a28f9a1edfcb0f9b ``` </Step> <Step title="5. As a result, the API returns a specified message."> ```curl theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&_id=5d76a618a28f9a1edfcb0f9b ``` </Step> </Steps> </Accordion> <Accordion title="Get message with reactions using API key (added in v2.58.0)"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&include_reactions=1 ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&include_reactions=1 ``` </Step> <Step title="3. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&include_reactions=1 ``` </Step> <Step title="4. Set the `include_reactions` query parameter"> To include detailed reactions data in the response, set the `include_reactions=1` query parameter. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&include_reactions=1 ``` </Step> <Step title="5. As a result, the API returns messages with reactions."> ```curl theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&include_reactions=1 ``` </Step> </Steps> </Accordion> <Accordion title="Filter messages by word with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&message[ctn]=guys ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user. ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&message[ctn]=guys ``` </Step> <Step title="3. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&message[ctn]=guys ``` </Step> <Step title="4. Set a search operator for a field"> Apply a search operator to the query. Here, a `ctn` operator is applied to a `message` field with the `guys` value. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&message[ctn]=guys ``` </Step> <Step title="5. As a result, the API will return messages satisfying the specified criteria."> All messages containing the guys word are returned for the specified dialog. ```curl theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&message[ctn]=guys ``` </Step> </Steps> </Accordion> <Accordion title="Filter and sort messages with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user. ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> <Step title="3. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> <Step title="4. Set a search operator for a field"> Apply a search operator to the query. Here, the `lt` operator is applied to a `date_sent` field. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> <Step title="5. Set a sort operator for a field"> Use a sort operator to order the search results by the field. Here, the `sort_desc` operator is applied to the `date_sent` field. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> <Step title="6. As a result, the API returns messages filtered and sorted upon the request."> The messages are returned with the **date\_sent** value less than **1568057349** and are sorted by the **date\_sent** field in **descending** order. ```curl theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e&date_sent[lt]=1568057349&sort_desc=date_sent ``` </Step> </Steps> </Accordion> </AccordionGroup> All retrieved chat messages will be marked as **read** after request.\ Messages will be marked in 2 cases: * if the current **user ID** is in `occupants_ids` field for `type=2` (GROUP) dialog; * if dialog's `type=1` (PUBLIC) dialog. **Search operators** The request can contain all, some or none of the next search operators: | Operator | Description | | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | lt | **Less Than** operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: date\_sent, sender\_id, recipient\_id, updated\_at <br /> <br />Example: <br /> <br />date\_sent\[lt]=1556829805 <br /> <br />Description: <br /> <br />Will return messages with `date_sent` **less than** 1556829805. | | lte | **Less Than or Equal** to operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: date\_sent, sender\_id, recipient\_id, updated\_at <br /> <br />Example: updated\_at\[lte]=1556829805 <br /> <br />Will return messages with `updated_at` **less than or equal** 1556829805. | | gt | **Greater Than** operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: date\_sent, sender\_id, recipient\_id, updated\_at <br /> <br />Example: <br />date\_sent\[gt]=1556829805 <br /> <br />Will return messages with `date_sent` **greater than** 1556829805. | | gte | **Greater Than or Equal** to operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: date\_sent, sender\_id, recipient\_id, updated\_at <br /> <br />Example: <br />date\_sent\[gte]=1556829805 <br /> <br />Will return messages with `date_sent` **greater than or equal** 1556829805. | | ne | **Not Equal** to operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: \_id, message, date\_sent, sender\_id, recipient\_id <br /> <br />Example: <br />recipient\_id\[ne]=9988237 <br /> <br />Will return messages with `recipient_id` **not equal** to 9988237. | | in | **Contained IN** array operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: date\_sent, sender\_id, recipient\_id <br /> <br />Example: <br />sender\_id\[in]=91419276,91419395 <br /> <br />Will return messages with `sender_id` **equal** to 91419276 and **equal** to 91419395. | | nin | **Not contained IN** array <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: date\_sent, sender\_id, recipient\_id <br /> <br />Example: <br />recipient\_id\[nin]=91419276,91419395 <br /> <br />Will return messages with `recipient_id` **not equal** to 91419276 and 91419395. | | or | **OR** operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: date\_sent, sender\_id, recipient\_id <br /> <br />Example: <br />sender\_id\[or]=91419390,96753878 <br /> <br />Will return messages with `sender_id` **equal** to 91419390 **or** 96753878. | | ctn | **Contains** substring operator <br /> <br />Types: string <br /> <br />Applicable to fields: message <br /> <br />Example: message\[ctn]=party <br /> <br />Returns messages which text **contains** the word "party". | **Sort operators** Next sorting operators are allowed: | Operator | Description | | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sort\_asc | Types: all types <br /> <br />Example: <br />sort\_asc=field\_name <br /> <br />Search results will be sorted by the specified field in ascending order. | | sort\_desc | Types: all types <br /> <br />Example: <br />sort\_desc=field\_name <br /> <br />Search results will be sorted by the specified field in descending order. | #### Query Parameters <ParamField type="string"> ID of the dialog. </ParamField> <ParamField type="string"> Search records with the field that contains the specified value. Can be many 1..N. </ParamField> <ParamField type="string"> Filters records with field which contains value according to specified value and operator. Can be many 1..N. </ParamField> <ParamField type="integer"> Mark the requested message as read. Set `mark_as_read=1 `to perform. As a result, when the message is retreived by the opponent, the opponent ID is added to the array of read\_ids field. Works only if the message had the unread status previously, not backwards. You can update message status using the [Update Message](/reference/list-messages#update-message) request. </ParamField> <ParamField type="string"> Limit search results to N records. Useful for pagination. </ParamField> <ParamField type="string"> Skip N records in search results. Useful for pagination. </ParamField> <ParamField type="string"> Count search results. Response will contain only a count of found records. </ParamField> <ParamField type="string"> Search results will be sorted by a specified field in descending order. </ParamField> <ParamField type="string"> Search results will be sorted by a specified field in ascending order. </ParamField> <ParamField type="integer"> Optional. Set to `1` to include reactions data in the message response. Default is `0`. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response <ResponseField name="skip" type="integer" /> <ResponseField name="limit" type="integer" /> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="_id" type="string" /> <ResponseField name="attachments" type="array"> <Expandable title="properties"> <ResponseField name="type" type="string" /> <ResponseField name="id" type="string" /> </Expandable> </ResponseField> <ResponseField name="chat_dialog_id" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="date_sent" type="integer" /> <ResponseField name="delivered_ids" type="array" /> <ResponseField name="message" type="string" /> <ResponseField name="read_ids" type="array" /> <ResponseField name="recipient_id" type="integer" /> <ResponseField name="sender_id" type="integer" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="read" type="integer" /> <ResponseField name="reactions" type="array of objects"> List of reactions added to the message. *This field is **omitted by default**. To include it, use the query parameter `?include_reactions=1`.* <Expandable title="properties"> <ResponseField name="name" type="string">Type of the reaction, e.g. "like", "love".</ResponseField> <ResponseField name="count" type="integer">Number of times this reaction was added.</ResponseField> <ResponseField name="user_ids" type="array of integers">IDs of users who added this reaction.</ResponseField> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: d23b555a6f9b6db4e3ebfd5e02a8e238e5012bba" \ https://api.quickblox.com/chat/Message.json?chat_dialog_id=5d75393ba28f9a17e1cb0f9e ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "skip": 0, "limit": 100, "items": [ { "_id": "5d755856a0eb47411e0b72a9", "attachments": [], "chat_dialog_id": "5d75393ba28f9a17e1cb0f9e", "created_at": "2019-09-08T19:36:54Z", "date_sent": 1567971414, "delivered_ids": [ 96753878 ], "message": "Hi guys", "read_ids": [ 96753878 ], "recipient_id": 0, "sender_id": 96753878, "updated_at": "2019-09-08T19:36:54Z", "read": 0 }, { "_id": "5d76a618a28f9a1edfcb0f9b", "age": "25", "attachments": [ { "type": "image", "id": "47863" }, { "type": "image", "id": "47863" } ], "chat_dialog_id": "5d75393ba28f9a17e1cb0f9e", "created_at": "2019-09-09T19:20:56Z", "date_sent": 1568056856, "delivered_ids": [ 96753878 ], "message": "Hi guys!", "read_ids": [ 96753878 ], "recipient_id": 343, "sender_id": 96753878, "updated_at": "2019-09-09T19:20:56Z", "read": 0 } ] } ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to perform this operation" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # List Reactions Source: https://docs.quickblox.com/reference/list-reactions GET https://api.quickblox.com/chat/Message/{chat_message_id}/reactions Retrieve all reactions for a specific message. <br/> *Added in v2.58.0* #### Recipes <AccordionGroup> <Accordion title="Get reactions for message"> <Steps> <Step title="1. Specify the message ID"> Provide the `message_id` of the message whose reactions you want to retrieve. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98/reactions ``` </Step> <Step title="2. As a result, the API returns a list of reactions."> ```curl theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98/reactions ``` </Step> </Steps> </Accordion> <Accordion title="Get reactions for message with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98/reactions ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98/reactions ``` </Step> <Step title="3. Specify the message ID"> Provide the `message_id` of the message whose reactions you want to retrieve. ```curl {4} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98/reactions ``` </Step> <Step title="4. As a result, the API returns a list of reactions."> ```curl theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98/reactions ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> ID of the message for which to retrieve reactions.</ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> 200 OK — Successful response <ResponseField name="total_entries" type="integer" /> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="name" type="string" /> <ResponseField name="count" type="integer" /> <ResponseField name="user_ids" type="array of integers" /> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ https://api.quickblox.com/chat/Message/5d75626ea28f9a1490cb0f98/reactions ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "total_entries": 2, "items": [ { "name": "like", "count": 3, "user_ids": [20618, 31841, 51234] }, { "name": "love", "count": 2, "user_ids": [20618, 51234] } ] } ``` ```json 400 theme={null} { "errors": [ "Invalid or missing \"message_id\" parameter." ] } ``` ```json 404 theme={null} { "errors": [ "Message not found for the specified user and message ID." ] } ``` </ResponseExample> # List Records Source: https://docs.quickblox.com/reference/list-records GET https://api.quickblox.com/data/{class_name}.json Search for records of a particular class. #### Recipes <AccordionGroup> <Accordion title="Filter records by location"> <Steps> <Step title="1. Set a search operator for a field"> Apply a search operator to the query. Here, the `near` operator is applied to a `user_location` field. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: b97d22aee7773587ede6b2120fda1d0d7c012bba" \ https://api.quickblox.com/data/ScoreTable.json?user_location[near]=11.00,45.40;16800 ``` </Step> <Step title="2. As a result, the API returns records filtered by the specified location."> All records with the `11.00,45.40;16800` value for the `user_location` field are turned. ```json theme={null} { "class_name": "ScoreTable", "skip": 0, "limit": 100, "items": [ { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.23, "score_value": 1000, "updated_at": 1574944158, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.12, 45.51 ] }, { "_id": "5d866a5aa0eb4753fc6099c3", "_parent_id": null, "completed_levels": [ 1, 2, 3, 10, 11, 12, 13 ], "created_at": 1569090138, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "4x4", "progress": 0.12, "score_value": 665, "updated_at": 1569189981, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ] } ] } ``` </Step> </Steps> </Accordion> <Accordion title="Filter records by criteria"> <Steps> <Step title="1. Set a search operator for a field"> Apply a search operator to the query. Here, the `lt` operator is applied to a `score_value` custom field. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 0bea0abd041b2431e5e1c2d0b06fab4414012bba" \ https://api.quickblox.com/data/ScoreTable.json?score_value[lt]=1000 ``` </Step> <Step title="2. As a result, the API returns records filtered upon the request."> All records with the `score_value` less than `1000` are returned. ```json theme={null} { "class_name": "ScoreTable", "skip": 0, "limit": 100, "items": [ { "_id": "5d866a5aa0eb4753fc6099c3", "_parent_id": null, "completed_levels": [ 1, 2, 3, 10, 11, 12, 13 ], "created_at": 1569090138, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing4", "progress": 0.12, "score_value": 665, "updated_at": 1569177165, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ] } ] } ``` </Step> </Steps> </Accordion> <Accordion title="Group records"> <Steps> <Step title="1. Set an aggregation operator for a field to calculate an average for all numeric values"> Apply an aggregation operator for a field to calculate an average for all numeric values. Here, the `calc` aggregation operator is applied to the `score_value` and set to the avg value. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 0bea0abd041b2431e5e1c2d0b06fab4414012bba" \ https://api.quickblox.com/data/ScoreTable.json?score_value[calc]=avg&group_by=game_mode_name ``` </Step> <Step title="2. Set an aggregation operator for a field to group records"> Apply an aggregation operator to a field to group records. Here, a group\_by operator is applied to a `game_mode_name` field. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 0bea0abd041b2431e5e1c2d0b06fab4414012bba" \ https://api.quickblox.com/data/ScoreTable.json?score_value[calc]=avg&group_by=game_mode_name ``` </Step> <Step title="3. As a result, the API returns records grouped according to the request."> The records are grouped by the `gamed_mode_name` field with an average calculated for the `score_value` field. ```json theme={null} { "class_name": "ScoreTable", "skip": 0, "limit": 100, "items": [ { "game_mode_name": "4x4", "avg": 665 }, { "game_mode_name": "beta testing", "avg": 2010.5 }, { "game_mode_name": "team rumble", "avg": 1211 } ] } ``` </Step> </Steps> </Accordion> <Accordion title="Filter records by location with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/data/ScoreTable.json?user_location[near]=11.00,45.40;16800 ``` </Step> <Step title="2. Set a search operator for a field"> Apply a search operator to the query. Here, the `near` operator is applied to a `user_location` field. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/data/ScoreTable.json?user_location[near]=11.00,45.40;16800 ``` </Step> <Step title="3. As a result, the API returns records filtered by the specified location."> All records with the `11.00,45.40;16800` value for the `user_location` field are turned. ```json theme={null} { "class_name": "ScoreTable", "skip": 0, "limit": 100, "items": [ { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.23, "score_value": 1000, "updated_at": 1574944158, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.12, 45.51 ] }, { "_id": "5d866a5aa0eb4753fc6099c3", "_parent_id": null, "completed_levels": [ 1, 2, 3, 10, 11, 12, 13 ], "created_at": 1569090138, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "4x4", "progress": 0.12, "score_value": 665, "updated_at": 1569189981, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ] } ] } ``` </Step> </Steps> </Accordion> <Accordion title="Filter records by criteria with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/data/ScoreTable.json?score_value[lt]=1000 ``` </Step> <Step title="2. Set a search operator for a field"> Apply a search operator to the query. Here, the `lt` operator is applied to a `score_value` custom field. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/data/ScoreTable.json?score_value[lt]=1000 ``` </Step> <Step title="3. As a result, the API returns records filtered upon the request."> All records with the `score_value` less than `1000` are returned. ```json theme={null} { "class_name": "ScoreTable", "skip": 0, "limit": 100, "items": [ { "_id": "5d866a5aa0eb4753fc6099c3", "_parent_id": null, "completed_levels": [ 1, 2, 3, 10, 11, 12, 13 ], "created_at": 1569090138, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing4", "progress": 0.12, "score_value": 665, "updated_at": 1569177165, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ] } ] } ``` </Step> </Steps> </Accordion> <Accordion title="Group records with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/data/ScoreTable.json?score_value[calc]=avg&group_by=game_mode_name ``` </Step> <Step title="2. Set an aggregation operator for a field to calculate an average for all numeric values"> Apply an aggregation operator for a field to calculate an average for all numeric values. Here, the `calc` aggregation operator is applied to the `score_value` and set to the avg value. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/data/ScoreTable.json?score_value[calc]=avg&group_by=game_mode_name ``` </Step> <Step title="3. Set an aggregation operator for a field to group records"> Apply an aggregation operator to a field to group records. Here, a group\_by operator is applied to a `game_mode_name` field. ```curl {4} theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/data/ScoreTable.json?score_value[calc]=avg&group_by=game_mode_name ``` </Step> <Step title="4. As a result, the API returns records grouped according to the request."> The records are grouped by the `gamed_mode_name` field with an average calculated for the `score_value` field. ```json theme={null} { "class_name": "ScoreTable", "skip": 0, "limit": 100, "items": [ { "game_mode_name": "4x4", "avg": 665 }, { "game_mode_name": "beta testing", "avg": 2010.5 }, { "game_mode_name": "team rumble", "avg": 1211 } ] } ``` </Step> </Steps> </Accordion> </AccordionGroup> <Info> If you are sorting records **by time**, use the `_id` field. It is indexed and will be much faster than the `created_at` field. </Info> Combinations of operators are allowed. For example:\ `score_value[gt]=100&score_value[lt]=1000&game_mode_name[in]=deathmatch,ctf` **Search Operators** The request can contain all, some or none of the next search operators: | Operator | Description | | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | lt | **Less Than** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />score\_value\[lt]=1000 | | lte | **Less Than or Equal to** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />score\_value\[lte]=850 | | gt | **Greater Than** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />bonus\_count\[gt]=2.45 | | gte | **Greater Than or Equal to** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />bonus\_count\[gte]=56.443 | | ne | **Not Equal to** operator <br /> <br />Types: integer, float, string, boolean <br /> <br />Example: <br />game\_mode\_name\[ne]=ctf | | in | **Contained IN array** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />game\_mode\_name\[in]=deathmatch,rage | | nin | **Not contained IN array** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />game\_mode\_name\[nin]=survivor,crazy\_nightmare | | all | **ALL contained IN array** operator <br /> <br />Types: array <br /> <br />Example: <br />game\_modes\[all]=survivor,crazy | | or | **OR** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />name\[or]=sam,tim <br />name\[or]=sam\&lastname\[or]=johnson <br /> <br />Will return records with name `sam` **or** `tim`. <br /> <br />Will return records with name `sam` **or** last name `johnson`. | | ctn | **Contains** substring operator <br /> <br />Types: string <br /> <br />Example: <br />username\[ctn]=son <br /> <br />Will return all records where `username` field **contains** `son` substring. | | near | Types: location <br /> <br />Example: <br />mylocation\[near]=25.32,44.551;1000 <br /> <br />Search records in a specific radius with <br />the current position in meters. <br />Format: `{field_name}[near]=longitude,latitude;radius`. | **Sort Operators** Next sorting operators are allowed: | Operator | Description | | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sort\_asc | Types: all types <br /> <br />Example: <br />sort\_asc=field\_name <br /> <br />Search results will be sorted by the specified field in ascending order. | | sort\_desc | Types: all types <br /> <br />Example: <br />sort\_desc=field\_name <br /> <br />Search results will be sorted by the specified field in descending order. | **Aggregation Operators** | Operator | Description | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \{field\_name}\[calc]= | Types: integer, <br />float <br /> <br />Example: <br />rating\[calc]=min <br /> <br />`avg`, `min`, `max`, `sum` can be used with `group_by` operator. | | group\_by=\{field\_name} | Types: integer, float, string, boolean <br /> <br />Example: <br />group\_by=game\_mode\_name <br /> <br />The `group_by` works similarly to the SQL GROUP BY operator, should be used with the `calc` operator. | #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> #### Query Parameters <ParamField type="string"> Search records with the field that contains the specified value. Can be many 1..N. </ParamField> <ParamField type="string"> Search a record with the field that contains a value according to the specified value and operator. </ParamField> <ParamField type="integer"> Limit search results to N records. Max(Cloud/Enterprise): 100/1000. Useful for pagination. </ParamField> <ParamField type="integer"> Skip N records in search results. Useful for pagination. </ParamField> <ParamField type="integer"> Count search results. Response will contain only count of records found. </ParamField> <ParamField type="string"> Search results will be sorted by specified field in descending order. </ParamField> <ParamField type="string"> Search results will be sorted by specified field in ascending order. </ParamField> <ParamField type="string"> The `output` parameter takes the form of a record with a list of fields for inclusion or exclusion from the result set. `output[include]` specifies the fields to include. The `_id` field is, by default, included in the result set. To exclude the `_id` field from the result set, you need to specify the exclusion of the `_id` field in the `output[exclude]` value. </ParamField> <ParamField type="string"> The `output` parameter takes the form of a record with a list of fields for inclusion or exclusion from the result set. `output[exclude]` specifies the fields to exclude. The `_id` field is, by default, included in the result set. To exclude the `_id` field from the result set, you need to specify the exclusion of the `_id` field in the `output[exclude]` value. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Response <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="class_name" type="string" /> <ResponseField name="skip" type="integer" /> <ResponseField name="limit" type="integer" /> <ResponseField name="items" type="array of objects" /> <Expandable title="properties"> <ResponseField name="_id" type="string" /> <ResponseField name="_parent_id" type="string" /> <ResponseField name="created_at" type="integer" /> <ResponseField name="updated_at" type="integer" /> <ResponseField name="user_id" type="integer" /> </Expandable> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "Content-Type: application/json" \ -H "QB-Token: 0bea0abd041b2431e5e1c2d0b06fab4414012bba" \ https://api.quickblox.com/data/ScoreTable.json?score_value[lt]=1000 ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "class_name": "ScoreTable", "skip": 0, "limit": 100, "items": [ { "_id": "5d866a5aa0eb4753fc6099c3", "_parent_id": null, "completed_levels": [ 1, 2, 3, 10, 11, 12, 13 ], "created_at": 1569090138, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing4", "progress": 0.12, "score_value": 665, "updated_at": 1569177165, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ] } ] } ``` </ResponseExample> # List Records by IDs Source: https://docs.quickblox.com/reference/list-records-by-ids GET https://api.quickblox.com/data/{class_name}/{custom_object_record_id},{custom_object_record_id}.json Retrieve records with particular records IDs. #### Recipes <AccordionGroup> <Accordion title="List records by IDs with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ https://api.quickblox.com/data/ScoreTable/5d866b53a28f9a5ad51cb562,5d867291a0eb473ed96099c4.json ``` </Step> <Step title="2. As a result, the API returns the records with the specified IDs."> ```json theme={null} { "class_name": "ScoreTable", "items": [ { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.23, "score_value": 1000, "updated_at": 1569178320, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } }, { "_id": "5d867291a0eb473ed96099c4", "_parent_id": null, "completed_levels": [ 1, 2, 3 ], "created_at": 1569092241, "date": "2019-09-21T21:25:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.3, "score_value": 3021, "updated_at": 1569178320, "user_avatar": null, "user_id": 96753878, "user_location": null, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ] } ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> <ParamField type="string"> ID of the custom object record. Generated automatically by the server after record creation. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="class_name" type="string" /> <ResponseField name="items" type="array of objects" /> <Expandable title="properties"> <ResponseField name="_id" type="string" /> <ResponseField name="_parent_id" type="string" /> <ResponseField name="created_at" type="integer" /> <ResponseField name="updated_at" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="permissions" type="object"> <Expandable title="properties"> <ResponseField name="read" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="update" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="delete" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Expandable> </Accordion> <Accordion title="400"> An error response </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: d9c91a69e552145f6fe24dbb40b50ac575012bba" \ https://api.quickblox.com/data/ScoreTable/5d866b53a28f9a5ad51cb562,5d867291a0eb473ed96099c4.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "class_name": "ScoreTable", "items": [ { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.23, "score_value": 1000, "updated_at": 1569178320, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } }, { "_id": "5d867291a0eb473ed96099c4", "_parent_id": null, "completed_levels": [ 1, 2, 3 ], "created_at": 1569092241, "date": "2019-09-21T21:25:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.3, "score_value": 3021, "updated_at": 1569178320, "user_avatar": null, "user_id": 96753878, "user_location": null, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ] } ``` ```json 400 theme={null} {} ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # List Subscriptions Source: https://docs.quickblox.com/reference/list-subscriptions GET https://api.quickblox.com/subscriptions.json Retrieve subscriptions for the device that is specified in the authorization token. #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response. <Expandable title="array of objects"> <ResponseField name="subscription" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="device" type="object"> <Expandable title="properties"> <ResponseField name="udid" type="string" /> <ResponseField name="client_identification_sequence" type="string" /> <ResponseField name="platform" type="object"> <Expandable title="properties"> <ResponseField name="name" type="string" /> </Expandable> </ResponseField> <ResponseField name="bundle_identifier" type="string" /> </Expandable> </ResponseField> <ResponseField name="notification_channel" type="object"> <Expandable title="notification_channel"> <ResponseField name="name" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Expandable> </Accordion> <Accordion title="422"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: 93d9dd94876d71dd39b56452cf28d80555012bba" \ https://api.quickblox.com/subscriptions.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} [ { "subscription": { "id": 59715257, "device": { "udid": "2343af3433aa3a4fsv", "client_identification_sequence": "1054929587f29c856fa0137f634cc0a5ec77901f4baaf2f3c1785d05ae114455", "platform": { "name": "ios" }, "bundle_identifier": "com.quickblox.myawesomeapp" }, "notification_channel": { "name": "apns" } } }, { "subscription": { "id": 59715699, "device": { "udid": "111111", "client_identification_sequence": "1054929587f29c856fa0137f634cc0a5ec77901f4baaf2f3c1785d05ae114455", "platform": { "name": "ios" }, "bundle_identifier": "test.app" }, "notification_channel": { "name": "apns" } } }, { "subscription": { "id": 59725669, "device": { "udid": "111111", "client_identification_sequence": "cw_edFOFLQc:APA91bGAeEwM0OddqErs-iD-s1ayCxzlF6HDwzzsZ6PYNJdHIRs0hkikNwFWAY1ImcHcO2Hy89kNyU4bB_TsvFwLGz2pPK05mIRe1bGOTvTNE5i6k0NUDuEIdF6FeQQvXUzVwLr52uk-", "platform": { "name": "android" }, "bundle_identifier": "test.app" }, "notification_channel": { "name": "gcm" } } }, { "subscription": { "id": 59725685, "device": { "udid": null, "client_identification_sequence": null, "platform": { "name": null }, "bundle_identifier": null }, "notification_channel": { "name": "email" } } } ] ``` ```json 400 theme={null} {} ``` </ResponseExample> # List Users Source: https://docs.quickblox.com/reference/list-users GET https://api.quickblox.com/users.json Retrieve all users for a current app. #### Recipes <AccordionGroup> <Accordion title="Filter users by multiple criteria"> <Steps> <Step title="1. Set a filter"> Use a `filter` parameter to set a filter. Set a value type, search operator, and value to filter users by. You can set as many filters as you need. Here, the two filters are set: 1. The users are filtered by the `facebook_id` field with a value less than the `50000`. 2. The users are filtered by the `created_at` field with a value greater than the `2019-08-27T21:08:55Z`. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: da6be1483635df69b2993352d053c7aa8b012bba" \ https://api.quickblox.com/users.json?filter[]=number+facebook_id+lt+50000&filter[]=date+created_at+gt+2019-08-27T21:08:55Z ``` </Step> <Step title="2. As a result, the API returns users filtered by two criteria."> 1. The users with the `facebook_id` value less than the `50000` are returned. 2. The users with the `created_at` value greater than the `2019-08-27T21:08:55Z` are returned. ```curl theme={null} curl -X GET \ -H "QB-Token: da6be1483635df69b2993352d053c7aa8b012bba" \ https://api.quickblox.com/users.json?filter[]=number+facebook_id+lt+50000&filter[]=date+created_at+gt+2019-08-27T21:08:55Z ``` </Step> </Steps> </Accordion> <Accordion title="Filter users by criteria"> <Steps> <Step title="1. Set a filter"> Use a `filter` parameter to set a filter. Set a value type, search operator, and value to filter users by. In this case, the users are filtered by the `facebook_id` with a value less than `50000`. ```curl {3} theme={null} curl -X GET \ -H "QB-Token: fdb5f5a28388a64aba5b2f57570b13f827012bba" \ https://api.quickblox.com/users.json?filter[]=number+facebook_id+lt+50000&page=2&per_page=2&order=asc+date+created_at ``` </Step> <Step title="2. As a result, the API returns users by the specified criteria."> The users with the `facebook_id` value greater than `50000` are returned. ```curl theme={null} curl -X GET \ -H "QB-Token: fdb5f5a28388a64aba5b2f57570b13f827012bba" \ https://api.quickblox.com/users.json?filter[]=number+facebook_id+lt+50000&page=2&per_page=2&order=asc+date+created_at ``` </Step> </Steps> </Accordion> <Accordion title="List users with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X GET \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users.json ``` </Step> <Step title="2. As a result, the API returns a list of users."> ```curl theme={null} curl -X GET \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users.json ``` </Step> </Steps> </Accordion> </AccordionGroup> **Search operators** The request can contain all, some or none of these parameters. If this option is set, its value is the object to validate. For example, if a `created_at` value is specified, the request will return only instances by the specified `created_at`value. Filters require an exact match of property values ​​with an instance of the corresponding parameter value. | Operator | Description | | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | gt | **Greater Than** operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id <br /> <br />Example: <br />filter\[]=number+external\_user\_id+gt+33 <br />filter\[]=date+updated\_at+gt+2019-05-23T18:07:19Z <br /> <br />Will return users with `external_user_id` **greater than** 33. <br />Will return users with updated\_at **greater than** 2019-05-23T18:07:19Z. | | lt | **Less Than** operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id <br /> <br />Example: <br />filter\[]=number+id+lt+85000 <br />filter\[]=date+last\_request\_at+lt+2019-05-23T18:07:19Z <br /> <br />Will return users with IDs **less than** 85000. <br />Will return users with `last_request_at` **less than** 2019-05-23T18:07:19Z. | | ge | **Greater or Equal** to operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id <br /> <br />Example: <br />filter\[]=number+facebook\_id+ge+34524 <br />filter\[]=date+last\_request\_at+ge+2019-05-23T18:07:19Z <br /> <br />Will return users with `facebook_id` **greater than or equal to** 34524. <br />Will return users with `last_request_at` **greater than or equal to** 2019-05-23T18:07:19Z. | | le | **Less or Equal** to operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id <br /> <br />Example: <br />filter\[]=number+facebook\_id+le+34524 <br />filter\[]=date+created\_at+le+2019-05-23T18:07:19Z <br /> <br />Will return users with `facebook_id` **less than or equal** 34524. <br />Will return users with created\_at **less than or equal** 2019-05-23T18:07:19Z. | | eq | **Equal** to operator <br /> <br />Types:number, string, date <br /> <br />Applicable to fields: id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id <br /> <br />Example: <br />filter\[]=number+facebook\_id+eq+810 <br />filter\[]=[string+email+eq+test@gmail.com](mailto:string+email+eq+test@gmail.com) <br />filter\[]=date+created\_at+eq+2019-04-22 <br /> <br />Will return a user with `facebook_id` **equal to** 810. <br />Will return a user with email **equal to** [test@gmail.com.](mailto:test@gmail.com.) <br />Will return a user with created\_at **equal to** 2019-04-22. | | ne | **Not Equal** to operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id <br /> <br />Example: <br />filter\[]=number+external\_user\_id +ne+810 <br />filter\[]=[string+email+ne+test@gmail.com](mailto:string+email+ne+test@gmail.com) <br />filter\[]=date+created\_at+ne+2019-04-22T19:07:19Z <br /> <br />Will return users with `external_user_id` **not equal to** 810. <br />Will return users with email **not equal to** [test@gmail.com.](mailto:test@gmail.com.) <br />Will return users with `created_at` **not equal to** 2019-04-22T19:07:19Z. | | between | **Contained Between** values operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id <br /> <br />Example: filter\[]=number+external\_user\_id+between+33,1000 <br />filter\[]=date+updated\_at+between+2019-04-22T19:07:19Z,2019-04-23T11:04:03Z <br /> <br />Will return users with `external_user_id` **between** 3 and 1000. <br />Will return users with `updated_at` **between** 2019-04-22T19:07:19Z and 2019-04-23T11:04:03Z. | | in | **Contained IN** array operator <br /> <br />Types: number, string, date <br /> <br />Applicable to fields: id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id <br /> <br />Example: <br />filter\[]=number+external\_user\_id+in+33,555 <br />filter\[]=[string+email+in+test@test.com](mailto:string+email+in+test@test.com),[test@gmail.com](mailto:test@gmail.com) <br />filter\[]=date+created\_at+in+2019-04-22T19:07:19Z,2019-04-22T19:08:38Z <br /> <br />Will return users **with** `external_user_id` 33 and 555. <br />Will return users **with** email [test@test.com](mailto:test@test.com) and [test@gmail.com.](mailto:test@gmail.com.) <br />Will return users **with** `created_at` 2019-04-22T19:07:19Z and 2019-04-22T19:08:38Z. | **Sort operators** There can be any of the below-specified parameters or nothing in the query as the value for sorting. If there are other parameters within the query, the validation error is returned. | Param | Possible values | Description | | ----- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | order | asc, desc | Types: string, number, date <br /> <br />Applicable to fields: id, full\_name, email, login, phone, website, created\_at, updated\_at, <br />last\_request\_at, external\_user\_id <br /> <br />Example: order=asc+date <br />+last\_request\_at <br /> <br />Will return users with the `last_request_at` sorted in the ascending order. | #### Query Parameters <ParamField type="string"> Review [Search Operators](#search-operators) section for more details. </ParamField> <ParamField type="string"> Review [Sort operators](#sort-operators) section for more details. </ParamField> <ParamField type="integer"> Page of results to return. </ParamField> <ParamField type="integer"> Number of records to return in one request. Min: 1. Max: 100. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="current_page" type="integer" /> <ResponseField name="per_page" type="integer" /> <ResponseField name="total_entries" type="integer" /> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: fdb5f5a28388a64aba5b2f57570b13f827012bba" \ https://api.quickblox.com/users.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "current_page": 1, "per_page": 10, "total_entries": 2, "items": [ { "user": { "id": 96753878, "full_name": "Peter Smith", "email": "peter@gmail.com", "login": "Peter", "phone": "+73123555", "website": "https://mysite.com", "created_at": "2019-08-27T20:49:07Z", "updated_at": "2019-09-01T12:23:08Z", "last_request_at": "2019-08-29T20:48:14Z", "external_user_id": 1236672, "facebook_id": "15868", "twitter_id": "9982366", "blob_id": null, "custom_data": "lead of marketing team", "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "Marketing,seo" } }, { "user": { "id": 96754523, "full_name": "Alex Miller", "email": "alex@gmail.com", "login": "Alex", "phone": "+3123555", "website": "https://mysite.com", "created_at": "2019-08-27T21:08:55Z", "updated_at": "2019-09-01T12:36:59Z", "last_request_at": null, "external_user_id": 126672, "facebook_id": "5844", "twitter_id": "982366", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "Android,web" } } ] } ``` ```json 403 theme={null} { "errors": { "base": [ "Users retrieving forbidden in admin panel, application settings. Setup it here https://admin.quickblox.com/apps/{app_id}/service/users/settings" ] } } ``` </ResponseExample> # List Users by Full Name Source: https://docs.quickblox.com/reference/list-users-by-full-name GET https://api.quickblox.com/users/by_full_name.json Search users by full name. #### Recipes <AccordionGroup> <Accordion title="List users by full name with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X GET \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_full_name.json?full_name=John%20Doe ``` </Step> <Step title="2. As a result, the API returns a list of users."> ```curl theme={null} curl -X GET \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_full_name.json?full_name=John%20Doe ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Query Parameters <ParamField type="string"> User's full name. Min 3 characters. </ParamField> <ParamField type="integer"> Page of results to return. </ParamField> <ParamField type="integer"> Number of records to return in one request. Min: 1. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="current_page" type="integer" /> <ResponseField name="per_page" type="integer" /> <ResponseField name="total_entries" type="integer" /> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: a1e6b25b27ab3771a391b5c1173b572aed012bba" \ https://api.quickblox.com/users/by_full_name.json?full_name=Alex%20Miller ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "current_page": 1, "per_page": 10, "total_entries": 1, "items": [ { "user": { "id": 96754523, "full_name": "Alex Miller", "email": "alex@gmail.com", "login": "Alex", "phone": "+3123555", "website": "https://mysite.com", "created_at": "2019-08-27T21:08:55Z", "updated_at": "2019-08-27T21:08:55Z", "last_request_at": null, "external_user_id": 126672, "facebook_id": "888766", "twitter_id": "982366", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "android,web" } } ] } ``` ```json 401 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` ```json 403 theme={null} { "errors": { "base": [ "Users retrieving forbidden in admin panel, application settings. Setup it here https://admin.quickblox.com/apps/{app_id}/service/users/settings" ] } } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find user" } ``` </ResponseExample> # List Users by Tags Source: https://docs.quickblox.com/reference/list-users-by-tags GET https://api.quickblox.com/users/by_tags.json Search users by tags. #### Recipes <AccordionGroup> <Accordion title="List users by tags with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X GET \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_tags.json?tags=android,web ``` </Step> <Step title="2. As a result, the API returns a list of users"> ```curl theme={null} curl -X GET \ -H 'Content-Type: application/json' \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/by_tags.json?tags=android,web ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Query Parameters <ParamField type="string"> User tags. The maximum number of tags per user is 5. </ParamField> <ParamField type="integer"> Page of results to return. </ParamField> <ParamField type="integer"> Number of records to return in one request. Min: 1. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="current_page" type="integer" /> <ResponseField name="per_page" type="integer" /> <ResponseField name="total_entries" type="integer" /> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: bdbb2bd5abb81b1d7fe7be03db9305b46e012bba" \ https://api.quickblox.com/users/by_tags.json?tags=android,web ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "current_page": 1, "per_page": 10, "total_entries": 2, "items": [ { "user": { "id": 96754523, "full_name": "Alex Miller", "email": "alex@gmail.com", "login": "Alex", "phone": "+3123555", "website": "https://mysite.com", "created_at": "2019-08-27T21:08:55Z", "updated_at": "2019-08-27T21:08:55Z", "last_request_at": null, "external_user_id": 126672, "facebook_id": "888766", "twitter_id": "982366", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "android,web" } }, { "user": { "id": 96831328, "full_name": "Morgan Fuller", "email": "morgan@gmail.com", "login": "Morgan", "phone": "+131234215", "website": "https://mysite.com", "created_at": "2019-08-29T19:13:49Z", "updated_at": "2019-08-29T19:13:49Z", "last_request_at": null, "external_user_id": 1712, "facebook_id": "18866", "twitter_id": "19366", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": true, "allow_sales_activities": true, "parents_contacts": "", "user_tags": "android,web" } } ] } ``` ```json 401 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` ```json 403 theme={null} { "errors": { "base": [ "Users retrieving forbidden in admin panel, application settings. Setup it here https://admin.quickblox.com/apps/{app_id}/service/users/settings" ] } } ``` ```json 404 theme={null} { "code": null, "message": "Couldn't find user" } ``` </ResponseExample> # Log In Source: https://docs.quickblox.com/reference/log-in POST https://api.quickblox.com/login.json Upgrades an application session to a user session. #### Recipes <AccordionGroup> <Accordion title="Log in with login and password"> <Steps> <Step title="1. Set the login"> Set your login here. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: cf5709d6013fdb7a6787fbeb8340afed8aec4c69" \ -d '{ "login":"Andrew", }' \ https://api.quickblox.com/login.json ``` </Step> <Step title="2. Set the password"> Set your password here. ```curl {6} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: cf5709d6013fdb7a6787fbeb8340afed8aec4c69" \ -d '{ "login":"Andrew", "password":"mypassword" }' \ https://api.quickblox.com/login.json ``` </Step> <Step title="3. As a result, the API returns a logged in user."> ```json theme={null} { "user": { "id": 96705448, "full_name": "Andrew Lee", "email": "andrew@gmail.com", "login": "Andrew", "phone": "+9923474236", "website": "https://myapp.com", "created_at": "2019-08-26T20:53:42Z", "updated_at": "2019-08-26T21:38:38Z", "last_request_at": "2019-08-26T21:38:44Z", "external_user_id": null, "facebook_id": "89987878", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": false, "allow_sales_activities": false, "parents_contacts": "", "user_tags": "admin,co-workers,design" } } ``` </Step> </Steps> </Accordion> <Accordion title="Log in with Firebase phone number"> <Steps> <Step title="1. Set a social network provider"> Set a `firebase_phone` as a social network provider. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 4b8d6b42d030ac4fc4251f9d0070ca5faa012bba" \ -d '{ "provider":"firebase_phone", }' \ https://api.quickblox.com/login.json ``` </Step> <Step title="2. Use a `firebase_phone` parameter to set Firebase data"> ```curl {6-7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 4b8d6b42d030ac4fc4251f9d0070ca5faa012bba" \ -d '{ "provider":"firebase_phone", "firebase_phone":{ } }' \ https://api.quickblox.com/login.json ``` </Step> <Step title="3. Set Firebase project ID"> Use a `project_id` parameter to set Firebase project ID. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 4b8d6b42d030ac4fc4251f9d0070ca5faa012bba" \ -d '{ "provider":"firebase_phone", "firebase_phone":{ "project_id":"my_app_project_id", } }' \ https://api.quickblox.com/login.json ``` </Step> <Step title="4. Set Firebase access token"> Use an `access_token` parameter to set Firebase access token. An access token is a token received from the social network after a user authenticates with it. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 4b8d6b42d030ac4fc4251f9d0070ca5faa012bba" \ -d '{ "provider":"firebase_phone", "firebase_phone":{ "project_id":"my_app_project_id", "access_token":"t35400dfzxcxvsdfn" } }' \ https://api.quickblox.com/login.json ``` </Step> <Step title="5. As a result, the API returns the logged in user."> ```json theme={null} { "user": { "id": 96705448, "full_name": "Andrew Lee", "email": "andrew@gmail.com", "login": "Andrew", "phone": "+9923474236", "website": "https://myapp.com", "created_at": "2019-08-26T20:53:42Z", "updated_at": "2019-08-26T21:38:38Z", "last_request_at": "2019-08-26T21:38:44Z", "external_user_id": null, "facebook_id": "89987878", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": false, "allow_sales_activities": false, "parents_contacts": "", "user_tags": "admin,co-workers,design" } } ``` </Step> </Steps> </Accordion> <Accordion title="Log in with Facebook access token"> <Steps> <Step title="1. Set a social network provider"> Use a `provider` param to set a social network provider. ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 4b8d6b42d030ac4fc4251f9d0070ca5faa012bba" \ -d '{ "provider":"facebook", }' \ https://api.quickblox.com/login.json ``` </Step> <Step title="2. Use a `keys` parameter to set an access token"> ```curl {6-7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 4b8d6b42d030ac4fc4251f9d0070ca5faa012bba" \ -d '{ "provider":"facebook", "keys":{ } }' \ https://api.quickblox.com/login.json ``` </Step> <Step title="3. Set an access token"> An access token is a token received from the social network after a user authenticates with it. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 4b8d6b42d030ac4fc4251f9d0070ca5faa012bba" \ -d '{ "provider":"facebook", "keys":{ "token":"AM46dxjhisdffgry26282352fdusdfusdfgsd" } }' \ https://api.quickblox.com/login.json ``` </Step> <Step title="4. As a result, the API returns a logged in user."> ```json theme={null} { "user": { "id": 96705448, "full_name": "Andrew Lee", "email": "andrew@gmail.com", "login": "Andrew", "phone": "+9923474236", "website": "https://myapp.com", "created_at": "2019-08-26T20:53:42Z", "updated_at": "2019-08-26T21:38:38Z", "last_request_at": "2019-08-26T21:38:44Z", "external_user_id": null, "facebook_id": "89987878", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": false, "allow_sales_activities": false, "parents_contacts": "", "user_tags": "admin,co-workers,design" } } ``` </Step> </Steps> </Accordion> </AccordionGroup> After an application session has been created, lets your users to log in to their account with login/email and password. There are the following login options available: * Log in with login/email and password. * Log in via Facebook. * Log in via Firebase phone number (SMS). #### Body Parameters <ParamField type="string"> User login. </ParamField> <ParamField type="string"> User email. </ParamField> <ParamField type="string"> User password. </ParamField> <ParamField type="string"> Possible providers: `facebook`, `firebase_phone`. </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> Social network provider's access token. </ParamField> <ParamField type="string"> Social network provider's access token secret. </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> A unique identifier for your Firebase project. </ParamField> <ParamField type="string"> Firebase user's ID token. </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. </ParamField> #### Responses <AccordionGroup> <Accordion title="202"> A successful response <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="string" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: cf5709d6013fdb7a6787fbeb8340afed8aec4c69" \ -d '{ "login":"Andrew", "password":"mypassword" }' \ https://api.quickblox.com/login.json ``` </RequestExample> <ResponseExample> ```json 202 theme={null} { "user": { "id": 96705448, "full_name": "Andrew Lee", "email": "andrew@gmail.com", "login": "Andrew", "phone": "+9923474236", "website": "https://myapp.com", "created_at": "2019-08-26T20:53:42Z", "updated_at": "2019-08-26T21:38:38Z", "last_request_at": "2019-08-26T21:38:44Z", "external_user_id": null, "facebook_id": "89987878", "blob_id": null, "custom_data": null, "age_over16": true, "allow_statistics_analysis": false, "allow_sales_activities": false, "parents_contacts": "", "user_tags": "admin,co-workers,design" } } ``` </ResponseExample> # Log Out Source: https://docs.quickblox.com/reference/log-out DELETE https://api.quickblox.com/login.json Downgrades a user session to an application session. <Warning> If you log out a user session, you downgrade to application session. Learn more about the difference between a user session and application session [here](/reference/authentication#application-vs-user-session-token). </Warning> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "QB-Token: 8b75a6c7191285499d890a81df4ee7fe49bc732a" \ https://api.quickblox.com/login.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 401 theme={null} { "errors": { "base": [ "Forbidden. Need user." ] } } ``` ```json 403 theme={null} { "errors": { "base": [ "Required session does not exist" ] } } ``` </ResponseExample> # Mark All Messages as Delivered Source: https://docs.quickblox.com/reference/mark-all-messages-as-delivered PATCH https://api.quickblox.com/chat/Message/delivered.json Update the delivered status of messages for a certain dialog. <br/> *Added in v2.39.0* #### Body Parameters <ParamField type="string"> ID of the dialog to which a current message is connected. Generated automatically by the server after message creation. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PATH \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/delivered.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to perform this operation" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # Mark All Messages as Read Source: https://docs.quickblox.com/reference/mark-all-messages-as-read PATCH https://api.quickblox.com/chat/Message/read.json Update the read status of messages for a certain dialog. <br/> *Added in v2.39.0* #### Body Parameters <ParamField type="string"> ID of the dialog to which a current message is connected. Generated automatically by the server after message creation. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PATH \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/read.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to perform this operation" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # Mark File Uploaded Source: https://docs.quickblox.com/reference/mark-file-uploaded PUT https://api.quickblox.com/blobs/{blob_id}/complete.json **DEPRECATED**. This request is deprecated. No need to use it to upload a file. Declare a file as uploaded. Set a file status to complete. If a specified file size does not match to the actual, the actual file size will be set. #### Path Parameters <ParamField type="integer"> ID of the file/blob. Generated automatically by the server after file/blob creation. </ParamField> #### Body Params <ParamField type="object"> <Expandable title="properties"> <ParamField type="integer"> Size of the uploaded file/blob in bytes. </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="blob" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "blob":{ "size":"84" } }' \ https://api.quickblox.com/blobs/10070416/complete.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 422 theme={null} { "errors": { "blob": [ "is not uploaded yet" ] } } ``` </ResponseExample> # Message Model Source: https://docs.quickblox.com/reference/messages | Property name | Type | Description | | ------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | \_id | string | ID of the message. Generated automatically by the server after message creation. | | created\_at | datetime | Date & time when a record was created, filled automatically. | | updated\_at | datetime | Date & time when a record was updated, filled automatically. | | chat\_dialog\_id | string | ID of dialog to which current message is connected. Generated automatically by the server after message creation. | | message | string | Message body. | | date\_sent | timestamp | Message date sent. | | sender\_id | int | Message sender ID. | | recipient\_id | int | Message recipient ID. | | read | int | (**Deprecated**) Read message status. Displayed as `read=1` after retiriving by the opponent. Works only for `type=3` (PRIVATE) dialog. Remains as `read=0` after retiriving for `type=2` (GROUP) and `type=1` (PUBLIC) dialogs. | | read\_ids\[] | array of int | Array of users' IDs who read messages. Works only for `type=2` (GROUP) dialog. | | delivered\_ids\[] | array of int | Array of users' IDs who received the messages. | | attachments\[] | array of objects | Each attachment object contains 3 required keys:`id` - link to file ID in QuickBlox,`type` - audio/video/image,`url` - link to file in Internet. | | \{custom\_field\_N} | any | Name of the custom field. Chat message can be extended with additional fields and contain any other user key-value custom parameters. Can be many 1..N. | # Overview Source: https://docs.quickblox.com/reference/overview QuickBlox API is comprised of different web resources representing data associated with chat activities. You can directly interact with these resources via dedicated actions over standard HTTP protocols. QuickBlox API is implemented based on the RESTful principles. REST is a certain specification that dictates how to implement and use the HTTP protocol. Thus, in response to HTTP requests, you get data back in the form of JSON payloads. Using Server API you can integrate QuickBlox functionality with your application. Use Server API for server-server connection or if your application programming language is not supported by QuickBlox SDKs. <Warning> For iOS, JavaScript and Android integration, use QuickBlox SDKs, not Server API. </Warning> #### Base URL Base URL is a value identifying particular API resource. This URL remains constant for all requests and constitutes the first half of the complete request URL. Use `https://api.quickblox.com` as the base URL. The second half of the resource URL is the endpoint representing a variable that must be set with appropriate values to access a specific resource. For example, `/login`. #### Headers A header is an integral part of each Server API request and response indicating information about request and response body and authorization. QuickBlox Server API provides two headers types: **Standard header** A `Content-type` API header that tells the server the media type of the request and is used for `PUT` and `POST` requests only. Thus, QuickBlox Server API supports `application/json` and `application/x-www-form-urlencoded` content type. **QuickBlox header** A `QB-Token` request header determines a unique value assigned to the current HTTP connection between the application and QuickBlox server. A `QB-Token-ExpirationDate` response header determines the token expiration date. #### URL encoding QuickBlox server uses automatic UTF-8 encoding to convert URL into a universally accepted and understood format. <Warning> Do NOT encode URL, the server does it for you. If the server receives a URL that already has been encoded by the client, the request will not be processed by the server. </Warning> # Permissions Source: https://docs.quickblox.com/reference/permissions Access control list (ACL) is a list of permissions attached to some object. An ACL specifies which users have access to objects as well as what operations are allowed on given objects. <Info> Access Control list available **only** for Custom Objects module. </Info> Each entry in a typical ACL specifies a subject and an operation. ACL models may be applied to collections of objects as well as to individual entities within the system hierarchy. ## Permission schema QuickBlox Permission schema contains **five** permissions levels: * **Open (open)** Such permission schema means that any user within the application can access the record/records in the class and is allowed to perform an action with the current permission level. * **Owner (owner)** Owner permission level means that only Owner (a user who created a record) is allowed to perform action with the current permission level. * **Not allowed (not\_allowed)** No one (except for the Account Administrator) can make a chosen action. * **Open for groups (open\_for\_groups)** Users having a specified tag/tags (see more info about how to set tags for the user in [this](/reference/user-model) section) will be included in the group that is allowed to perform an action with the current permission level. The current permission level can consist of one or several groups (number of groups is **not** limited). Tags can be added/deleted in the user’s profile. * **Open for user ids (open\_for\_users\_ids)** Only users that are specified in the permission level can make a required action with a record. One or several users can be specified (the number of users is **not** limited). ## Actions available for the entity * **Create** Create a record. * **Read** Retrieve and read the info about the chosen record. * **Update** Update any parameter for the chosen record (only those parameters that can be set by the user can be updated). * **Delete** Delete a record. ## Permission levels There are two access levels in the Permissions schema: **Class** and **Record**. ### Class entity Only the Account Administrator can create a class in the Custom object module and make all possible actions with it. Operations with **Class** entity are not allowed in API. All actions (Create, Read, Update, and Delete) are available for the class entity and are applicable for all records in the class. Every action has a separate permission level available. The exception is a Create action that is not available for the **Owner** permission level. To set a permission schema for the Class, do the following: 1. Go to the **Custom Objects** tab. 2. Open a required class. 3. Click **Edit permissions** button to open a class and edit it. <Frame> <img alt="1690" /> </Frame> Default Class permission schema is used while creating a class: * **Create**: Open * **Read**: Open * **Update**: Owner * **Delete**: Owner <Info> Mark checkboxes to **enable** class permissions. </Info> ### Record entity A record is an entity within the class in the Custom Objects module that has its own permission levels. You can create a record in the Dashboard and API (see the [create record](/reference/create-record) request for more details). All permission levels except for the **Not Allowed** are available for the record and there are only three actions available and applicable for the record: Read, Update, and Delete. Default Record permission schema is used while creating a class: * **Read**: Open * **Update**: Owner * **Delete**: Owner To set a permission level open the required Class and click the record to edit it. <Frame> <img alt="812" /> </Frame> ## Choosing a permission schema Only **one** permission level can be applicable to the record: class permission schema or record permission schema. To apply class permission levels to **all** records in the class tick the checkbox in the **Use Class permissions** column near the required Action in the Dashboard. <Frame> <img alt="734" /> </Frame> <Info> Using a class permission schema means that a record permission schema will **not** affect a reсord. </Info> <Info> In case, the Admin **does not** tick the checkbox in the Dashboard a user **has** a possibility to change permission levels for every separate record in the table or create a new one with the ACL that a user requires. </Info> # Push Notification Formats Source: https://docs.quickblox.com/reference/push-notification-formats QuickBlox provides **two** types of push notifications you can send: 1. **Platform based push notification** will be delivered to the specified platform only, for example, iOS or Android only. 2. **Universal push notifications** will be delivered to all possible devices/platforms for specified users. ## Platform-based push notification A platform-based push notification will be delivered to a specified platform only, for example, iOS or Android. To specify platform use `event.push_type` parameter within **Create Event** request. With platform-based push notification, you can use all specified features of a particular platform. There are no restrictions. ### General requirements A message format is a `key=value` string where a `key` is raw text and `value` is **CGI escaped** and **Base64 encoded**. Each pair should be separated by `&`. **Example:** Plain message: `key1=c29tZXZhbHVlMQ==&key2=YW5vdGhlcnZhbHVlMg==&key3=dGhpcmRleGFtcGxl` <Warning> Symbol `&` should be escaped by %26. </Warning> ### iOS To meet Apple payload requirements review [this document](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW1). **Example:** 1. Initial JSON payload: `{ "aps" : { "alert" : "You got your emails.", "badge" : 9, "sound" : "bingbong.aiff" }, "acme1" : "bar", "acme2" : 42 }` 2. Base64 Encoded data: `ew0KICAgICJhcHMiIDogew0KICAgICAgICAiYWxlcnQiIDogIllvdSBnb3QgeW91ciBlbWFpbHMuIiwNCiAgICAgICAgImJhZ GdlIiA6IDksDQogICAgICAgICJzb3VuZCIgOiAiYmluZ2JvbmcuYWlmZiINCiAgICB9LA0KICAgICJhY21lMSIgOiAiYmFyIiwNCiAg ICAiYWNtZTIiIDogNDINCn0=` 3. Final message (for iOS pushes you should add `payload=` before `message`): `event.message=payload=ew0KICAgICJhcHMiIDogew0KICAgICAgICAiYWxlcnQiIDogIllvdSBnb3QgeW91ciBlbWFpbHMuIiwNCiAgICAgICAg ImJhZGdlIiA6IDksDQogICAgICAgICJzb3VuZCIgOiAiYmluZ2JvbmcuYWlmZiINCiAgICB9LA0KICAgICJhY21lMSIgOiAiYmFyIiwNCi AgICAiYWNtZTIiIDogNDINCn0=` ### Android To meet FCM requirements review [this document](https://firebase.google.com/docs/cloud-messaging/). The overall principles are as follows: 1. `data.message` key is required and should be **first**. 2. Values should be **CGI escaped** before **Base64 encoding**. 3. The required field `collapse_key` is added automatically before sending and contains value `event<ID>`. 4. Message format is a `data.key1=value1&...&data.keyN=valueN`. **Example:** 1. Initial plain message: `data.message=I love M&M's! Especially red one!` 2. CGI-escaped: `data.message=I+love+M%26M%27s%21+Especially+red+one%21` 3. Base64-encoded: `data.message=SStsb3ZlK00lMjZNJTI3cyUyMStFc3BlY2lhbGx5K3JlZCtvbmUlMjE=` 4. Final message: `event.message=data.message=SStsb3ZlK00lMjZNJTI3cyUyMStFc3BlY2lhbGx5K3JlZCtvbmUlMjE=` ## Universal Push Notifications Universal push notifications will be delivered to all possible devices/platforms for specified users. To send Universal push notifications just omit `event.push_type` parameter within a **Create Event** request. ### Send a simple text If you would like to send just a text push message (without any parameters) use the format below. **Example:** 1. Initial plain message: `I love M&M's! Especially red one!` 2. Base64-encoded: `SSBsb3ZlIE0mTSdzISBFc3BlY2lhbGx5IHJlZCBvbmUh` 3. Final message: `event.message=SSBsb3ZlIE0mTSdzISBFc3BlY2lhbGx5IHJlZCBvbmUh` ### Use custom parameters <Warning> Custom parameters are available for iOS and Android platforms only. </Warning> With custom parameters, you can achieve a behavior similar to platform-based push notifications. There are some standard parameters, which will be translated to particular platform parameters: * `message` push text will be translated to `aps.alert.body` for iOS and to `data.message` for Android. * `ios_badge` will be translated to `aps.badge` for iOS. Ignored for Android. * `ios_sound` will be translated to `aps.sound` for iOS. Ignored for Android. * `ios_content_available=1` will be translated to `aps.content-available` for iOS. Ignored for Android. * `ios_mutable_content=1` will be translated to `aps.mutable-content` for iOS. Ignored for Android. * `ios_category` will be translated to `aps.category` for iOS. Ignored for Android. * `ios_voip=1` will initiate VoIP push notification for iOS if user has VoIP push subscription. Otherwise, iOS user will receive standard iOS push. For Android, it will be a standard push. You can use any other custom parameters. They will be added as well according to the specific platform push format. For iOS, it will be root keys, for Android - `data.X`. **Example:** 1. Initial JSON message: `{"message": "Message received from Bob", "ios_badge": 5, "ios_sound": "mysound.wav", "user_id": "234"}` 2. Base64-encoded: `c29tZXZhbHVlMQc29tZXZhbHVlMQc29tZXZhbHVlMQc29tZXZhbHVlMQc29tZXZhbHVlMQ` 3. Final message: `event.message=c29tZXZhbHVlMQc29tZXZhbHVlMQc29tZXZhbHVlMQc29tZXZhbHVlMQc29tZXZhbHVlMQ` **Client's application will receive next payload:** <CodeGroup> ```objectivec iOS theme={null} - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { NSLog(@"New push: %@", userInfo); } ... { aps = { alert = "Message received from Bob"; badge = 5; sound = "mysound.wav"; }; "user_id" = 234; } ``` ```java Android theme={null} @Override protected void onMessage(Context context, Intent intent) { String message = ""; for(String key : intent.getExtras().keySet()){ Log.d(LOG_TAG, key + ": " + intent.getExtras().getString(key)); } } ... 07-21 11:07:26.489 14443-14842/? D/GCMIntentService﹕ message: Message+received+from+Bob 07-21 11:07:26.489 14443-14842/? D/GCMIntentService﹕ user_id: 234 07-21 11:07:26.489 14443-14842/? D/GCMIntentService﹕ collapse_key: event1206083 07-21 11:07:26.489 14443-14842/? D/GCMIntentService﹕ from: 761750217637 ``` </CodeGroup> # Push Notifications Source: https://docs.quickblox.com/reference/push-notifications Push Notifications module provides powerful and simple to use API to subscribe your users to push & email notifications and send them. * **Offline messages**. Send a chat message when a recipient is offline. * **Offline calls**. Make a video/audio call with offline opponents. * **Requests to contact list.** Send requests to add a user to the contact list. * **Messages Queue**. Notifications can be sent either instantly or assigned a specific time/date and pushed into a Message Queue allowing to send delayed and recurring messages such as planned marketing announcements or any planned notifications for the better engagement of the users. * **Recurring notifications**. Set up a time interval and the message will be repeated automatically by the server. * **User tags**. Send notifications to specific user groups defined by tags. # Reaction Model Source: https://docs.quickblox.com/reference/reaction-model > **Added in v2.58.0** | Property name | Type | Description | | ------------- | ------------ | ----------------------------------------------------------------- | | name | string | Type of reaction (e.g., "like", "love", "laugh"). | | count | int | Total number of times this reaction has been used on the message. | | user\_ids | array of int | List of user IDs who have added this reaction to the message. | # Record Model Source: https://docs.quickblox.com/reference/record-model | Field name | Type | Description | | ------------------- | -------- | ------------------------------------------------------------------------------ | | \_id | string | ID of the record. Generated automatically by the server after record creation. | | user\_id | int | ID of the user who created the record. | | parent\_id | string | ID of parent object ([relations](/reference/relations)). | | created\_at | datetime | Date & time when a record was created, filled automatically. | | updated\_at | datetime | Date & time when record was updated, filled automatically. | | \{custom\_field\_N} | any | Your custom field. Can be many 1..N. | # Relations Source: https://docs.quickblox.com/reference/relations It is possible to create a relation between objects of two different classes via `_parent_id` field. For example, we have the class **Rating** that contains `score`, `review`, and `comment` fields. We also have a **Movie** class. So we can create a record of class **Rating** that will point to the record of the class **Movie** via its `_parent_id` field, so the `_parent_id` field will contain the ID of record from class **Movie**. <Warning> This is not a simple soft link. This is actually a **hard link**. When you delete the **Movie** class record then all its children (records of class **Rating** with `_parent_id` field set to the **Movie** class record ID) will be automatically **deleted** as well. See [this request](/reference/create-child-record) to learn how to create a child custom record. </Warning> <Info> If you need to retrieve all children, you can retrieve records with the filter `_parent_id=<id_of_parent_class_record>`. </Info> # Remove Reaction Source: https://docs.quickblox.com/reference/remove-reaction DELETE https://api.quickblox.com/chat/Message/{chat_message_id}/reactions Remove a previously added reaction from a message. <br/> *Added in v2.58.0* #### Recipes <AccordionGroup> <Accordion title="Remove a reaction from message"> <Steps> <Step title="1. Set the message ID"> Provide the `message_id` of the message from which you want to remove a reaction. ```curl {7} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="2. Set the reaction type to remove"> Provide the `name` of the reaction that should be removed. It must match the reaction previously added by the same user. ```curl {5} theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="3. As a result, the API returns a '200 OK' status code, indicating that the reaction was removed."> ```curl theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: 2e80acd7f3d9c1e79312e0d1dafd6691ba012bba" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> </Steps> </Accordion> <Accordion title="Remove a reaction from message with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> ```curl {3} theme={null} curl -X GET \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="3. Set the message ID"> Provide the `message_id` of the message from which you want to remove a reaction. ```curl {7} theme={null} curl -X DELETE \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="4. Set the reaction type to remove"> Provide the `name` of the reaction that should be removed. It must match the reaction previously added by the same user. ```curl {5} theme={null} curl -X DELETE \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> <Step title="5. As a result, the API returns a '200 OK' status code, indicating that the reaction was removed."> ```curl theme={null} curl -X DELETE \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name": "like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> ID of the message. Generated automatically by the server after message creation. </ParamField> #### Body Parameters <ParamField type="string"> The type of reaction to remove. Examples: `"like"`, `"love"`, `"haha"`, `"angry"`. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> Reaction successfully removed </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X DELETE \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "name":"like" }' \ https://api.quickblox.com/chat/Message/5d75327ba28f9a0f32cb0f99/reactions ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 400 theme={null} // Missing or invalid "message_id" parameter. { "errors": [ "Invalid or missing \"message_id\" parameter." ] } // Missing or invalid "name" parameter. { "errors": [ "Invalid or missing \"name\" parameter." ] } ``` ```json 404 theme={null} // Reaction not found { "errors": [ "Reaction not found for the specified user and message ID." ] } // Message not found { "errors": [ "Message not found for the specified user and message ID." ] } ``` </ResponseExample> # Reset User Password Source: https://docs.quickblox.com/reference/reset-user-password GET https://api.quickblox.com/users/password/reset.json Reset a user password by email. #### Recipes <AccordionGroup> <Accordion title="Reset user password with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/password/reset.json?email=usermail@quickblox.com ``` </Step> <Step title="2. As a result, the API send reset password instruction"> ```curl theme={null} curl -X GET \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ https://api.quickblox.com/users/password/reset.json?email=usermail@quickblox.com ``` </Step> </Steps> </Accordion> </AccordionGroup> <Info> By default, this feature is turned off for Basic, Startup and Growth plans. [Contact the sales team](https://quickblox.com/enterprise/#get). </Info> #### Query Parameters <ParamField type="string">User's email.</ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="401"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="404"> An error response </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="id" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X GET \ -H "QB-Token: 1e0bad9d9830c038ea19f12d83fca4e984012bba" \ https://api.quickblox.com/users/password/reset.json?email=usermail@quickblox.com ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 401 theme={null} { "errors": { "base": [ "You are not allowed to modify this user" ] } } ``` ```json 404 theme={null} null ``` ```json 422 theme={null} { "errors": { "id": [ "should be a positive integer" ] } } ``` </ResponseExample> # Session Model Source: https://docs.quickblox.com/reference/session-model | Property name | Type | Description | | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | id | int | ID of the session. Generated automatically by the server after session creation. | | application\_id | int | ID of the user's application. | | user\_id | int | ID of the session's owner. | | created\_at | datetime | Date & time when a record was created, filled automatically. | | updated\_at | datetime | Date & time when a record was updated, filled automatically. | | nonce | int | Unique Random Value. Parameter from a session creating request is used. | | token | string | Session identifier. Each API request should contain this parameter in QB-Token header. | | ts | int | Unix Timestamp. Parameter from session creating request is used. | | \_id | string | ID of the session. Generated automatically by the server after session creation.Date & time when a record was updated, filled automatically. | # Subscription Model Source: https://docs.quickblox.com/reference/subscription-model | Property name | Type | Description | | --------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | id | int | ID of the subscription. Generated automatically by the server after subscription creation. | | notification\_channel | nested object | Declare which notification channels could be used to notify user about events. | | notification\_channel.name | string | Notification channel name. Possible values are: `apns`, `apns_voip`, `gcm`, `email`. | | device | nested object | Device details which subscribes to receive notifications. | | device.client\_identification\_sequence | string | Unique hash to identify a client device in third-party service such as APNs or GCM/FCM. Initially retrieved from third-party service and should be sent to QuickBlox to let it consequently send push notifications to the client. | | device.platform | nested object | A platform of a device that is a source of the application running. | | device.platform.name | string | Platform name. Possible values are: `ios` and `android`. | | device.bundle\_identifier | string | A unique identifier for client's application. In iOS, this is a Bundle Identifier. In Android, a package ID. | | device.udid | string | UDID (Unique Device Identifier) of a device, which is the source of application running. This must be any sequence identifying a particular device. This is needed to support schema: 1 User => Multiple devices. | # Tips and Tricks Source: https://docs.quickblox.com/reference/tips-and-tricks #### Sort by ID field The `_id` field contains info about creation timestamp. It means that sorting on an `_id` field is roughly equivalent to sorting by `created_at` field, but works much faster because `_id` field has predefined index. Hence it is highly recommended to use sort by `_id` field instead of sort by `created_at` field: <CodeGroup> ```bash curl theme={null} curl -X GET -d "sort_desc=_id&limit=10" -H "QB-Token: 4d617908bf432bd0a72e2e089ed500e76a2c1a3b" https://api.quickblox.com/data/Message ``` </CodeGroup> #### Skip parameter performance The `skip` parameter is often expensive because it requires the server to walk from the beginning of the collection to get the offset or skip position before beginning to return result. As offset increases, skip will become slower and more CPU intensive. With larger collections, skip may become IO bound. Instead of using big value of skip try to improve your query. Maybe you don't need it. # Update Dialog Source: https://docs.quickblox.com/reference/update-dialog PUT https://api.quickblox.com/chat/Dialog/{chat_dialog_id}.json Update a dialog. ### Recipes <AccordionGroup> <Accordion title="Update dialog name"> <Steps> <Step title="1. Set a name of the dialog"> You can set a `name` field for a `group` or `public` dialog only. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "name":"New Year 2020 party" }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {7} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "name":"New Year 2020 party" }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. As a result, the API returns a dialog with an updated name."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "name":"New Year 2020 party" }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Add participants to the dialog"> <Steps> <Step title="1. Append participants to the array with participants IDs"> Use a `push_all` operator to append participants to the array with participants IDs. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Set the participants that should be added to the dialog"> Set the participants IDs to an `occupants_ids` array to add participants to the dialog. ```curl {6-9} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {12} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="4. As a result, the API returns a dialog with new dialog participants."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Update custom data of the dialog"> <Steps> <Step title="1. Set a custom object that should be updated"> Use a `data` parameter to refer to a custom object that should be updated. The data stores data as a set of unique keys each having an associated value parameter. See [this section](/reference/create-class) to learn how to create a custom object class. ```curl {5-11} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 8be2595b87f403f3c39e38fb39942cf481012bba" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="2. Set a name of the class"> Here, we refer to the `CoolDialog` custom object class. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 8be2595b87f403f3c39e38fb39942cf481012bba" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="3. Specify custom object fields and their new values"> Here, we refer to custom object fields of the `Cooldialog` class and set new values to these fields. ```curl {7-10} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 8be2595b87f403f3c39e38fb39942cf481012bba" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="4. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {13} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 8be2595b87f403f3c39e38fb39942cf481012bba" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="5. As a result, the API returns a dialog with updated custom data."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 8be2595b87f403f3c39e38fb39942cf481012bba" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> </Steps> </Accordion> <Accordion title="Remove participants from dialog"> <Steps> <Step title="1. Specify if you want to remove dialog participants"> Use a `pull_all` operator to remove participants from the array with participants IDs. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Set the participants that should be removed from the dialog"> Set participants IDs to an `occupants_ids` array to remove the participants from the dialog. ```curl {6-9} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {2} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="4. As a result, the API returns a dialog with an updated 'occupants_ids' array list."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Update dialog name with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name":"New Year 2024 party" }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name":"New Year 2024 party" }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Set a name of the dialog"> You can set a name field for a group or public dialog only. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name":"New Year 2024 party" }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="4. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {8} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name":"New Year 2024 party" }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="5. As a result, the API returns a dialog with an updated name."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "name":"New Year 2024 party" }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Update dialog owner (added in v2.39.0)"> <Steps> <Step title="1. Set user_id of the dialog"> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "user_id": 96753877 }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Set the ID of the dialog that should be updated"> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "user_id": 96753877 }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. As a result, the API returns a dialog with an updated user_id."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "user_id": 96753877 }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Add admins to the dialog (added in v2.52.0)"> <Steps> <Step title="1. Append admins to the array with participants IDs"> Use a `push_all` operator to append admins to the array with participants IDs. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Set the participants that should be added to the dialog"> Set the participants IDs to an `admin_ids` array to add participants to the dialog. ```curl {6-9} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {12} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="4. As a result, the API returns a dialog with new dialog admins."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Remove admins from dialog (added in v2.52.0)"> <Steps> <Step title="1. Specify if you want to remove dialog admins"> Use a `pull_all` operator to remove admins from the array with participants IDs. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Set the admins that should be removed from the dialog"> Set participants IDs to an `admin_ids` array to remove the participants from the dialog. ```curl {6-9} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {12} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="4. As a result, the API returns a dialog with an updated 'admin_ids' array list."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Add participants to the dialog with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Append participants to the array with participants IDs"> Use a `push_all` operator to append participants to the array with participants IDs. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="4. Set the participants that should be added to the dialog"> Set the participants IDs to an `occupants_ids` array to add participants to the dialog. ```curl {7-10} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="5. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {13} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="6. As a result, the API returns a dialog with new dialog participants."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Remove participants from dialog with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Specify if you want to remove dialog participants"> Use a `pull_all` operator to remove participants from the array with participants IDs. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="4. Set the participants that should be removed from the dialog"> Set participants IDs to an `occupants_ids` array to remove the participants from the dialog. ```curl {7-10} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="5. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {13} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="6. As a result, the API returns a dialog with an updated 'occupants_ids' array list."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "occupants_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Update custom data of the dialog with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="3. Set a custom object that should be updated"> Use a data parameter to refer to a custom object that should be updated. The data stores data as a set of unique keys each having an associated value parameter. See this section to learn how to create a custom object class. ```curl {6-12} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="4. Set a name of the class"> Here, we refer to the `CoolDialog` custom object class. ```curl {7} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="5. Specify custom object fields and their new values"> Here, we refer to custom object fields of the `Cooldialog` class and set new values to these fields. ```curl {8-11} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="6. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {14} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> <Step title="7. As a result, the API returns a dialog with updated custom data."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "data":{ "class_name":"CoolDialog", "category":"co-workers", "floor":"5", "is_intern":"true", "average_kpi":"0.8" } }' \ https://api.quickblox.com/chat/Dialog/5cc748eca0eb470d452adaaa.json ``` </Step> </Steps> </Accordion> <Accordion title="Update is_join_required parameter (added in v2.34.0)"> <Steps> <Step title="1. Set is_join_required value"> You can update the `is_join_required` field for `GROUP` dialogs only (`type=2`). Possible values: * `0` — join not required * `1` — join required ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "is_join_required": 0 }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {7} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "is_join_required": 0 }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Result"> The API returns the updated dialog with the modified `is_join_required` value. ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "is_join_required": 0 }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Update dialog owner (added in v2.39.0)"> <Steps> <Step title="1. Set user_id of the dialog"> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "user_id": 96753877 }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Set the ID of the dialog that should be updated"> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "user_id": 96753877 }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. As a result, the API returns a dialog with an updated user_id."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "user_id": 96753877 }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Add admins to the dialog with API key (added in v2.52.0)"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Append admins to the array with participants IDs"> Use a `push_all` operator to append admins to the array with participants IDs. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="4. Set the admins that should be added to the dialog"> Set the admins IDs to an `admin_ids` array to add participants to the dialog. ```curl {7-10} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="5. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {13} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="6. As a result, the API returns a dialog with new dialog admins."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "push_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> <Accordion title="Remove admins from dialog with API key (added in v2.52.0)"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="3. Specify if you want to remove dialog admins"> Use a `pull_all` operator to remove admins from the array with participants IDs. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="4. Set the admins that should be removed from the dialog"> Set admins IDs to an `admin_ids` array to remove the participants from the dialog. ```curl {7-10} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="5. Set the ID of the dialog that should be updated"> To get a dialog ID, use a [List Dialogs](/reference/list-dialogs) request. ```curl {13} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> <Step title="6. As a result, the API returns a dialog with an updated 'admin_ids' array list."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -H 'On-Behalf-Of: 123' \ -d '{ "pull_all":{ "admin_ids":[ 91375295, 96705448 ] } }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </Step> </Steps> </Accordion> </AccordionGroup> The following rules are applied if dialog's `type=3` (PRIVATE): * **Any** user from `occupants_ids` can update custom parameters. The following rules are applied if dialog's `type=2` (GROUP): <br /> Admin Support (introduced in v2.52.0) * **Any** user from `occupants_ids` can add **other** users. * **Any** user from `occupants_ids` can remove only **himself**. * **Any** user from `occupants_ids` can update custom parameters. * Dialog's **owner** (creator) and **admin** can remove **any** users from `occupants_ids`. * Only dialog's **owner** (creator) can add **other** users to `admin_ids`. * Only dialog's **owner** (creator) can remove **any** users from `admin_ids`. * **Admins** can remove themselves from `admin_ids`. The following rules are applied if dialog's `type=1` (PUBLIC\_GROUP): * Dialog’s **owner** (creator) and **admin** can update it. * Only dialog's **owner** (creator) can add **other** users to `admin_ids`. * Only dialog's **owner** (creator) can remove **any** users from `admin_ids`. * **Admins** can remove themselves from `admin_ids`. #### Path Parameters <ParamField type="string"> ID of the dialog to which a current message is connected. Generated automatically by the server after message creation</ParamField> #### Body Parameters <ParamField type="string"> Name of a dialog. The maximum length is 200 symbols. </ParamField> <ParamField type="string"> Photo of the dialog. </ParamField> <ParamField type="integer"> Defines whether a user must perform a join operation before sending and receiving messages via XMPP. Applies only to `type=2` (GROUP). Possible values: <br /> `0` — join not required, <br /> `1` — join required. <br /> (added in v2.34.0). </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="array of integers"> Update dialog occupants. Use `push_all` operator to **add** new occupants. </ParamField> <ParamField type="array of integers"> Update dialog admins. Use `push_all` operator to **add** new admins.(added in v2.52.0) </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="array of integers"> Update dialog occupants. Use `pull_all` to **remove** occupants. </ParamField> <ParamField type="array of integers"> Update dialog admins. Use `pull_all` to **remove** admins.(added in v2.52.0) </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> Class name in Custom Objects. </ParamField> <ParamField type="string"> Field name of class in Custom Objects. Can be many: 1..N. </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="_id" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="last_message" type="string" /> <ResponseField name="last_message_date_sent" type="integer" /> <ResponseField name="last_message_id" type="string" /> <ResponseField name="last_message_user_id" type="integer" /> <ResponseField name="name" type="string" /> <ResponseField name="occupants_ids" type="array of integers" /> <ResponseField name="admin_ids" type="array of integers (added in v2.52.0)" /> <ResponseField name="photo" type="string" /> <ResponseField name="type" type="integer" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="xmpp_room_jid" type="string" /> <ResponseField name="unread_messages_count" type="string" /> <ResponseField name="is_join_required" type="integer" /> </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "name":"New Year 2020 party" }' \ https://api.quickblox.com/chat/Dialog/5d75393ba28f9a17e1cb0f9e.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "_id": "5d75393ba28f9a17e1cb0f9e", "created_at": "2019-09-08T17:24:11Z", "last_message": "sure, I can wait", "last_message_date_sent": 1567971414, "last_message_id": "5d755856a0eb47411e0b72a9", "last_message_user_id": 96753878, "name": "New Year 2020 party", "occupants_ids": [ 96753878, 96754523, 96830631, 96831328, 96831397 ], "admin_ids": [], "photo": "68123", "type": 2, "updated_at": "2019-09-08T19:36:54Z", "user_id": 96753878, "xmpp_room_jid": "76730_5d75393ba28f9a17e1cb0f9e@muc.chat.quickblox.com", "unread_messages_count": 0, "is_join_required": 1 } ``` ```json 400 theme={null} { "errors": [ "Users are already present in occupants_ids" ] } // If an invalid value is provided when modifying the is_join_required parameter { "errors": [ "Parameter \"is_join_required\" should be a number: either 0 or 1." ] } ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to perform this operation" ] } // If a user who is neither the account owner nor the dialog owner attempts to modify the is_join_required parameter { "errors": [ "You do not have permission to modify the \"is_join_required\" parameter." ] } { "errors": [ "You don't have appropriate permissions to modify or delete \"admin_ids\". " ] } ``` </ResponseExample> # Update Event Source: https://docs.quickblox.com/reference/update-event PUT https://api.quickblox.com/events/{event_id}.json Edit event. #### Path Parameters <ParamField type="integer">ID of the event. </ParamField> #### Body Parameters <ParamField type="object"> <Expandable title="event object"> <ParamField type="boolean"> Marks event as active/inactive. Possible values are: `true` and `false`. </ParamField> <ParamField type="string"> Encoded message payload. Can be Base64 encoded plain text or push notification payload ([Push notifications formats](/reference/push-notification-formats)). </ParamField> <ParamField type="date-time"> The date of the event. If `event_type=fixed_date`, the date cannot be in the past. </ParamField> <ParamField type="date-time"> The period of the event in seconds. </ParamField> <ParamField type="string"> The name of the event. </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="event" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="event_type" type="string" /> <ResponseField name="message" type="string" /> <ResponseField name="date" type="string" /> <ResponseField name="period" type="string" /> <ResponseField name="name" type="string" /> <ResponseField name="occured_count" type="integer" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="end_date" type="string" /> <ResponseField name="active" type="boolean" /> <ResponseField name="application_id" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="kind" type="string" /> <ResponseField name="environment" type="string" /> <ResponseField name="tag_query" type="string" /> <ResponseField name="notification_channel" type="object"> <Expandable title="properties"> <ResponseField name="name" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response </Accordion> <Accordion title="422"> An error response <Expandable title="Option 1"> <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="period" type="array of strings" /> <ResponseField name="end_date" type="array of strings" /> <ResponseField name="name" type="array of strings" /> </Expandable> </ResponseField> </Expandable> <Expandable title="Option 2"> <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="date" type="array of strings" /> </Expandable> </ResponseField> </Expandable> <Expandable title="Option 3"> <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="event_type" type="array of strings" /> </Expandable> </ResponseField> </Expandable> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 37d3a10a1843ac780cb6f2c683bddad2b0012bba" \ -d '{ "event":{ "date":"1568839180", "period":"86400", "name":"My New Event" } }' \ https://api.quickblox.com/events/30529342.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "event": { "id": 30529342, "event_type": "period_date", "message": "payload=eyJhcHMiOnsiYWxlcnQiOiJQdXNoIG5vdGlmaWNhdGlvbiBmcm9tIFRheWxvciIsInNvdW5kIjoiZGVmYXVsdCJ9fQ==", "date": 1568839180, "period": 86400, "name": "My New Event", "occured_count": 0, "created_at": "2019-09-01T20:19:08Z", "updated_at": "2019-09-03T19:45:42Z", "end_date": 1569359375, "active": false, "application_id": 76730, "user_id": 91141808, "kind": "API", "environment": "production", "tag_query": null, "notification_channel": { "name": "apns" } } } ``` ```json 400 theme={null} {} ``` ```json 422 theme={null} { "errors": { "event_type": [ "is not included in the list" ] } } ``` </ResponseExample> # Update File Source: https://docs.quickblox.com/reference/update-file PUT https://api.quickblox.com/blobs/{blob_id}.json Update file metadata and/or its content. #### Recipes <AccordionGroup> <Accordion title="Replace file with new file"> <Steps> <Step title="1. Use a `blob` parameter to set blob fields"> ```curl {5-6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ } }' \ https://api.quickblox.com/blobs/10119859.json ``` </Step> <Step title="2. Update a file content to new"> Set a `new to 1` in the blob to update the file content. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ "new":"1" } }' \ https://api.quickblox.com/blobs/10119859.json ``` </Step> <Step title="3. As a result, the file is replaced with a new one."> ```json theme={null} { "blob": { "id": 10119859, "uid": "5b40013845324e798902f9bf94fc687b00", "content_type": "image/jpeg", "name": "apple.jpeg", "size": 6743, "created_at": "2020-09-15T11:12:00Z", "updated_at": "2020-09-15T11:12:12Z", "blob_status": null, "set_completed_at": "2020-09-15T11:12:02Z", "public": true, "account_id": 102190, "app_id": 74829, "blob_object_access": { "id": 10119859, "blob_id": 10119859, "expires": "2020-09-15T12:12:12Z", "object_access_type": "Write", "params": "https://api.quickblox.com/blobs/10119859/upload/?acl=public-read&content_type=image%2Fjpeg&expires=2020-09-15%2012%3A12%3A12%20UTC&key=5b40013845324e798902f9bf94fc687b00&policy=YTAyMTM4ZGUyNzk2ZWRmZDA4MTg0NjQ2OGUwYTZhNjk%3D%0A&success_action_status=201&x-amz-algorithm=AWS4-HMAC-SHA256&x-amz-credential=160e31c718a12f1b46e8b347fea52e4d%2F20200915%2Fqbprod%2Fs3%2Faws4_request&x-amz-date=2020259T1212Z&x-amz-signature=a6d47838690dffcec8cb0a7d2059faaa" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Update file name"> <Steps> <Step title="1. Use a `blob` parameter to set blob fields"> ```curl {5-6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ } }' \ https://api.quickblox.com/blobs/9715927.json ``` </Step> <Step title="2. Set a new file name"> Set a new file `name` in the blob. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: f8dd521e8a88b0df7e0c359dc3de73fc15012bba" \ -d '{ "blob":{ "name":"apple" } }' \ https://api.quickblox.com/blobs/9715927.json ``` </Step> <Step title="3. As a result, the API returns a file with an updated name."> ```json theme={null} { "blob": { "id": 9715867, "uid": "9dc0d37c9be34c839ae3bcac4bbaace100", "content_type": "image/jpeg", "name": "apple", "size": 16766, "created_at": "2019-09-15T19:40:18Z", "updated_at": "2019-09-15T20:35:45Z", "blob_status": "complete", "set_completed_at": "2019-09-15T20:00:46Z", "public": false } } ``` </Step> </Steps> </Accordion> </AccordionGroup> Update a file name, content type or file tag list. You can also update the file content by setting `new=1` parameter. Once set, the file is deleted in the cloud storage and a new record is created. Consequently, all file fields, except for the `name`, are updated in the cloud storage including the `uid`, `size`, etc. #### Path Parameters <ParamField type="integer"> ID of the file/blob. Generated automatically by the server after file/blob creation. </ParamField> #### Body Parameters <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> <Tooltip> MIME content type</Tooltip> </ParamField> <ParamField type="string">File/blob name</ParamField> <ParamField type="string"> Should be a comma-separated string with tags.</ParamField> <ParamField type="string"> Use it to update the content of the blob file. Set `new=1` to proceed.</ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="blob" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="uid" type="string" /> <ResponseField name="content_type" type="string" /> <ResponseField name="name" type="string" /> <ResponseField name="size" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="blob_status" type="string" /> <ResponseField name="set_completed_at" type="string" /> <ResponseField name="public" type="boolean" /> </Expandable> </ResponseField> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: ee0959fe70ddaf8d1a04e8ed6425bdd9f4012bba" \ -d '{ "blob":{ "name":"apple" } }' \ https://api.quickblox.com/blobs/9715927.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "blob": { "id": 9715867, "uid": "9dc0d37c9be34c839ae3bcac4bbaace100", "content_type": "image/jpeg", "name": "apple", "size": 16766, "created_at": "2019-09-15T19:40:18Z", "updated_at": "2019-09-15T20:35:45Z", "blob_status": "complete", "set_completed_at": "2019-09-15T20:00:46Z", "public": false } } ``` ```json 403 theme={null} { "errors": [ "Forbidden" ] } ``` </ResponseExample> # Update Message Source: https://docs.quickblox.com/reference/update-message PUT https://api.quickblox.com/chat/Message/{chat_message_id}.json Update a dialog message. #### Recipes <AccordionGroup> <Accordion title="Mark messages as delivered"> <Steps> <Step title="1. Specify if you want to mark messages as delivered"> Set a `delivered` parameter to `1` to mark all messages as delivered. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="2. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="3. As a result, the API retuns a '200 OK' status code which implies that all messages of the specified dialog were marked as delivered."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> </Steps> </Accordion> <Accordion title="Update message text"> <Steps> <Step title="1. Type an updated message"> Enter an updated message text using the `message` parameter. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="2. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="3. Set the ID of the message"> Set the ID of the message to refer to a particular message. ```curl {8} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="4. As a result, the API returns '200 OK' status code which implies that the message was successfully updated."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> </Steps> </Accordion> <Accordion title="Mark messages as read"> <Steps> <Step title="1. Specify if you want to mark messages as read."> Set a `read` parameter to `1` to mark all messages as read. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="2. Set the ID of the dialog."> Set the `chat_dialog_id` to refer to a specific dialog. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="3. As a result, the API retuns a '200 OK' status code which implies that all messages of the specified dialog were marked as read."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> </Steps> </Accordion> <Accordion title="Mark messages as delivered with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="3. Specify if you want to mark messages as delivered"> Set a `delivered` parameter to `1` to mark all messages as delivered. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="4. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {7} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="5. As a result, the API retuns a '200 OK' status code which implies that all messages of the specified dialog were marked as delivered."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> </Steps> </Accordion> <Accordion title="Update message text with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="3. Type an updated message"> Enter an updated message text using the `message` parameter. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="4. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {7} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="5. Set the ID of the message"> Set the ID of the message to refer to a particular message. ```curl {9} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="6. As a result, the API returns '200 OK' status code which implies that the message was successfully updated."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "message":"updated message", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> </Steps> </Accordion> <Accordion title="Mark messages as read with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="3. Specify if you want to mark messages as read"> Set a `read` parameter to `1` to mark all messages as read. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="4. Set the ID of the dialog."> Set the `chat_dialog_id` to refer to a specific dialog. ```curl {7} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> <Step title="5. As a result, the API retuns a '200 OK' status code which implies that all messages of the specified dialog were marked as read."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </Step> </Steps> </Accordion> <Accordion title="Mark all messages as read"> <Steps> <Step title="1. Specify if you want to mark messages as read."> Set a `read` parameter to `1` to mark all messages as read. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="2. Set the ID of the dialog."> Set the `chat_dialog_id` to refer to a specific dialog. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="3. Don't set any message ID to the path"> To mark all messages of specified dialog as read keep the path without message ID ```curl {8} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="4. As a result, the API retuns a '200 OK' status code which implies that all messages of the specified dialog were marked as read."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> </Steps> </Accordion> <Accordion title="Mark all messages as delivered"> <Steps> <Step title="1. Specify if you want to mark messages as delivered"> Set a `delivered` parameter to `1` to mark all messages as delivered. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="2. Set the ID of the dialog"> Set a `chat_dialog_id` to refer to a specific dialog. ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="3. Don't set any message ID to the path"> To mark all messages of specified dialog as delivered keep the path without message ID ```curl {8} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> <Step title="4. As a result, the API retuns a '200 OK' status code which implies that all messages of the specified dialog were marked as delivered."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "delivered":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message.json ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> ID of the message. Generated automatically by the server after message creation. </ParamField> #### Body Parameters <ParamField type="integer"> Update a message status to read. Set `read=1` to perform. As a result, the user ID is added to the array of `read_ids` field. See the [Messages](/reference/chat#messages) model for more details. </ParamField> <ParamField type="integer"> Update a message status to delivered. Set `delivered=1` to perform. As a result, the user ID is added to the array of `delivered_ids` field. See the [Messages](/reference/chat#messages) model for more details. </ParamField> <ParamField type="string"> Update message's text. </ParamField> <ParamField type="string"> ID of the dialog containing a message for update. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: eddf864695d72d33b959eec2ae6c640d817dfada" \ -d '{ "read":"1", "chat_dialog_id":"53a99a7be4b094c7c6d31b41" }' \ https://api.quickblox.com/chat/Message/53aabe15e4b077ddd43e7fd3.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} Empty body ``` ```json 403 theme={null} { "errors": [ "You don't have appropriate permissions to perform this operation" ] } ``` ```json 404 theme={null} { "errors": [ "The resource wasn't found" ] } ``` </ResponseExample> # Update Multi Records Source: https://docs.quickblox.com/reference/update-multi-records PUT https://api.quickblox.com/data/{class_name}/multi.json Update multiple records. #### Recipes <AccordionGroup> <Accordion title="Update multiple records with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "record":{ "1":{ "id":"5d866b53a28f9a5ad51cb562", "score_value":"1000" }, "2":{ "id":"5d867291a0eb473ed96099c4", "progress":"0.3", "completed_levels":[ 1, 2, 3 ] } } }' \ https://api.quickblox.com/data/ScoreTable/multi.json ``` </Step> <Step title="2. As a result, the API returns the updated records."> ```json theme={null} { "class_name": "ScoreTable", "not_found": { "ids": [] }, "items": [ { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.23, "score_value": 1000, "updated_at": 1569178320, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } }, { "_id": "5d867291a0eb473ed96099c4", "_parent_id": null, "completed_levels": [ 1, 2, 3 ], "created_at": 1569092241, "date": "2019-09-21T21:25:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.3, "score_value": 3021, "updated_at": 1569178320, "user_avatar": null, "user_id": 96753878, "user_location": null, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ] } ``` </Step> </Steps> </Accordion> </AccordionGroup> **Allowed permissions** | Permission | Syntax | Example | | ------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Open | permissions.\<CRUD\_operation>.access | permissions.read.access=open | | Owner | permissions.\<CRUD\_operation>.access | permissions.read.access=owner | | Open for users IDs | permissions.\<CRUD\_operation>.access=open\_for\_users\_ids permissions.\<CRUD\_operation>.ids=id\_1,id\_2,id\_3,… | permissions.update.access=open\_for\_users\_ids permissions.update.ids=3,12 | | Open for groups | permissions.\<CRUD\_operation>.access=open\_for\_groups permissions.\<CRUD\_operation>.groups=group\_name\_1,group\_name\_2 | permissions.delete.access=open\_for\_groups permissions.delete.groups=experience,rate | #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> #### Body Parameters <ParamField type="object"> <Expandable title="properties"> <ParamField type="object"> Numbering records in the query. Begin with 1 to N where N is the last number of the record. <Expandable title="properties"> <ParamField type="string"> ID of the record. </ParamField> <ParamField type="string"> Update value to the field defined in Custom Object class. </ParamField> <ParamField type="string"> Record permissions. Format: <br /> `permission.<CRUD_operation>.access=<value>` <br /> `permission.<CRUD_operation>.<option>=<value>` CRUD operations: `create`, `read`, `update`, `delete`. <br /> CRUD access values: `open`, `owner`, `open_for_users_ids`, `open_for_groups`. <br /> CRUD options: `ids`, `groups`. </ParamField> </Expandable> </ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="class_name" type="string" /> <ResponseField name="not_found" type="object"> <Expandable title="properties"> <ResponseField name="ids" type="array of strings" /> </Expandable> </ResponseField> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="_id" type="string" /> <ResponseField name="_parent_id" type="string" /> <ResponseField name="created_at" type="integer" /> <ResponseField name="updated_at" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="permissions" type="object"> <Expandable title="properties"> <ResponseField name="read" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="update" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="delete" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: c3cd44309db1405472627e7a5cb436278e012bba" \ -d '{ "record":{ "1":{ "id":"5d866b53a28f9a5ad51cb562", "score_value":"1000" }, "2":{ "id":"5d867291a0eb473ed96099c4", "progress":"0.3", "completed_levels":[ 1, 2, 3 ] } } }' \ https://api.quickblox.com/data/ScoreTable/multi.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "class_name": "ScoreTable", "not_found": { "ids": [] }, "items": [ { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.23, "score_value": 1000, "updated_at": 1569178320, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } }, { "_id": "5d867291a0eb473ed96099c4", "_parent_id": null, "completed_levels": [ 1, 2, 3 ], "created_at": 1569092241, "date": "2019-09-21T21:25:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.3, "score_value": 3021, "updated_at": 1569178320, "user_avatar": null, "user_id": 96753878, "user_location": null, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ] } ``` ```json 400 theme={null} {} ``` </ResponseExample> # Update Notification Settings Source: https://docs.quickblox.com/reference/update-notification-settings PUT https://api.quickblox.com/chat/Dialog/{chat_dialog_id}/notifications.json Update dialog notification settings for current user. #### Recipes <AccordionGroup> <Accordion title="Update Notification Settings with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "enabled":0 }' \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </Step> <Step title="2. Use 'On-Behalf-Of' header to pass user_id"> To execute a request on behalf of a specific user. ```curl {4} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "enabled":0 }' \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </Step> <Step title="3. Set settings"> "enabled": 0 - turn off the push notifications for specified dialog <br /> "enabled": 1 - turn off the push notifications for specified dialog ```curl {6} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "enabled":0 }' \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </Step> <Step title="4. Set dialog ID"> Specify the ID of the dialog whose setting status you want to update. ```curl {8} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "enabled":0 }' \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </Step> <Step title="5. As result, the API returns updated notification settings status"> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -H "On-Behalf-Of: 123" \ -d '{ "enabled":0 }' \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </Step> </Steps> </Accordion> </AccordionGroup> A user can turn on/off push notifications for offline messages in a dialog. The default value is ON. By default, when a user is offline and other user sent a message to them, they receive a push notification. It is possible to disable this feature. Available only for dialogs `type=2` (GROUP) and `type=3` (PRIVATE). #### Path Parameters <ParamField type="string"> ID of the dialog to which a current message is connected. Generated automatically by the server after message creation</ParamField> #### Body Parameters <ParamField type="integer"> Possible values are: `enabled=0`, `enabled=1`. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> <ParamField type="string">User ID. The user ID of the user on whose behalf the request is being made.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="notifications" type="object"> <Expandable title="properties"> <ResponseField name="enabled" type="integer" /> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 1319b4991186601ccf4df2d0fa89565f68012bba" \ -d '{ "enabled":0 }' \ https://api.quickblox.com/chat/Dialog/5d75651fa0eb4732190b72aa/notifications.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "notifications": { "enabled": 0 } } ``` ```json 400 theme={null} { "errors": [ "Incorrect params." ] } ``` </ResponseExample> # Update Record Source: https://docs.quickblox.com/reference/update-record PUT https://api.quickblox.com/data/{class_name}/{custom_object_record_id}.json Update an existing record. #### Recipes <AccordionGroup> <Accordion title="Add/remove value from record array"> <Steps> <Step title="1. Set an update operator for an array field to add values to the array"> Apply an `add_to_set` operator to an array field to add values to the array. Here, the `add_to_set` operator is applied to the `films` field with new values that should be added to the array. ```curl {5-10} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: c3cd44309db1405472627e7a5cb436278e012bba" \ -d '{ "add_to_set":{ "films":[ "The Shawshank Redemption", "Aladdin" ] }, "pull":{ "ratings":{ "lt":3 } } }' \ "https://api.quickblox.com/data/Cinemas/5d87a916a28f9a683f1cb55d.json" ``` </Step> <Step title="2. Set an update operator for another array field to remove values from the array"> Apply a `pull` operator to the array field to remove values from the array. Here, the `pull` operator is applied to the `ratings` field with the values that should be removed from its array. ```curl {11-15} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: c3cd44309db1405472627e7a5cb436278e012bba" \ -d '{ "add_to_set":{ "films":[ "The Shawshank Redemption", "Aladdin" ] }, "pull":{ "ratings":{ "lt":3 } } }' \ "https://api.quickblox.com/data/Cinemas/5d87a916a28f9a683f1cb55d.json" ``` </Step> <Step title="3. As a result, the API returns the updated record."> The specified values are added to the `films` array and removed from the `ratings` array. ```json theme={null} { "_id": "5d87a916a28f9a683f1cb55d", "_parent_id": null, "created_at": 1569248534, "films": [ "The Shawshank Redemption", "Aladdin" ], "ratings": [], "updated_at": 1569252134, "user_id": 96753878, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Increment/append value of record array"> <Steps> <Step title="1. Set an update operator for an array field to increment values of an array"> Apply the `inc` operator to an array field to increment its values. Here, the `inc` operator is applied to the `score_value` and `progress` fields. ```curl {5-8} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: c3cd44309db1405472627e7a5cb436278e012bba" \ -d '{ "inc":{ "score_value":20, "progress":0.11 }, "push":{ "completed_levels":[ 4 ] } }' \ "https://api.quickblox.com/data/ScoreTable/5d866b53a28f9a5ad51cb562.json" ``` </Step> <Step title="2. Set an update operator for another array field to append values to it"> Apply a `push` operator to the array to append values to it. Here, the `push` operator is applied to the `completed_levels` field with the values that should be appended. ```curl {9-13} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: c3cd44309db1405472627e7a5cb436278e012bba" \ -d '{ "inc":{ "score_value":20, "progress":0.11 }, "push":{ "completed_levels":[ 4 ] } }' \ "https://api.quickblox.com/data/ScoreTable/5d866b53a28f9a5ad51cb562.json" ``` </Step> <Step title="3. As a result, the API returns the updated record."> The `4` value is appended to the `completed_levels` array while the `score_value` and `progress` values are incremented by the `20` and `0.11`. ```json theme={null} { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "progress": 0.34, "score_value": 1020, "updated_at": 1569252134, "user_id": 96753878, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Update record array element by index"> <Steps> <Step title="1. Set an array element"> Set an array element that should be updated. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: c3cd44309db1405472627e7a5cb436278e012bba" \ -d '{ "movie[<1>]":"Titatic", }' \ "https://api.quickblox.com/data/ScoreTable/5f60e245a28f9a78e6c07322.json" \ ``` </Step> <Step title="2. As a result, the API returns an updated array."> The 1st element of the `movie` array is updated to `Titanic`. ```json theme={null} { "_id": "5f60e245a28f9a78e6c07322", "_parent_id": null, "created_at": 1600184901, "movie": [ "Star Wars", "Titanic" ], "updated_at": 1600184901, "user_id": 102433734, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Update record"> <Steps> <Step title="1. Set a new value for a field"> You can update a value of the field. Here, the `game_mode_name` field is set to `rainbow dash`. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-token: c8d706877dc40c4f56f8be618dda898456013152" \ -d '{ "game_mode_name":"rainbow dash", }' \ "https://api.quickblox.com/data/ScoreTable/5f60e245a28f9a78e6c07322.json" \ ``` </Step> <Step title="2. As a result, the API returns an updated record."> ```json theme={null} { "_id": "5f60e245a28f9a78e6c07322", "_parent_id": null, "created_at": 1600184901, "expert_mode": null, "game_mode_name": "rainbow dash", "name": null, "progress": 0, "score_value": null, "updated_at": 1600184901, "user_id": 102433734, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Add/remove value from record array with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "add_to_set":{ "films":[ "The Shawshank Redemption", "Aladdin" ] }, "pull":{ "ratings":{ "lt":3 } } }' \ "https://api.quickblox.com/data/Cinemas/5d87a916a28f9a683f1cb55d.json" ``` </Step> <Step title="2. Set an update operator for an array field to add values to the array"> Apply an `add_to_set` operator to an array field to add values to the array. Here, the `add_to_set` operator is applied to the `films` field with new values that should be added to the array. ```curl {5-10} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "add_to_set":{ "films":[ "The Shawshank Redemption", "Aladdin" ] }, "pull":{ "ratings":{ "lt":3 } } }' \ "https://api.quickblox.com/data/Cinemas/5d87a916a28f9a683f1cb55d.json" ``` </Step> <Step title="3. Set an update operator for another array field to remove values from the array"> Apply a `pull` operator to the array field to remove values from the array. Here, the `pull` operator is applied to the `ratings` field with the values that should be removed from its array. ```curl {11-15} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "add_to_set":{ "films":[ "The Shawshank Redemption", "Aladdin" ] }, "pull":{ "ratings":{ "lt":3 } } }' \ "https://api.quickblox.com/data/Cinemas/5d87a916a28f9a683f1cb55d.json" ``` </Step> <Step title="4. As a result, the API returns the updated record."> The specified values are added to the `films` array and removed from the `ratings` array. ```json theme={null} { "_id": "5d87a916a28f9a683f1cb55d", "_parent_id": null, "created_at": 1569248534, "films": [ "The Shawshank Redemption", "Aladdin" ], "ratings": [], "updated_at": 1569252134, "user_id": 96753878, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Increment/append value of record array with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "inc":{ "score_value":20, "progress":0.11 }, "push":{ "completed_levels":[ 4 ] } }' \ "https://api.quickblox.com/data/ScoreTable/5d866b53a28f9a5ad51cb562.json" ``` </Step> <Step title="2. Set an update operator for an array field to increment values of an array"> Apply the `inc` operator to an array field to increment its values. Here, the `inc` operator is applied to the `score_value` and `progress` fields. ```curl {5-8} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "inc":{ "score_value":20, "progress":0.11 }, "push":{ "completed_levels":[ 4 ] } }' \ "https://api.quickblox.com/data/ScoreTable/5d866b53a28f9a5ad51cb562.json" ``` </Step> <Step title="3. Set an update operator for another array field to append values to it"> Apply a `push` operator to the array to append values to it. Here, the `push` operator is applied to the `completed_levels` field with the values that should be appended. ```curl {9-13} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "inc":{ "score_value":20, "progress":0.11 }, "push":{ "completed_levels":[ 4 ] } }' \ "https://api.quickblox.com/data/ScoreTable/5d866b53a28f9a5ad51cb562.json" ``` </Step> <Step title="4. As a result, the API returns the updated record."> The `4` value is appended to the `completed_levels` array while the `score_value` and `progress` values are incremented by the `20` and `0.11`. ```json theme={null} { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "progress": 0.34, "score_value": 1020, "updated_at": 1569252134, "user_id": 96753878, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Update record array element by index with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "movie[<1>]":"Titatic", }' \ "https://api.quickblox.com/data/ScoreTable/5f60e245a28f9a78e6c07322.json" \ ``` </Step> <Step title="2. Set an array element"> Set an array element that should be updated. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "movie[<1>]":"Titatic", }' \ "https://api.quickblox.com/data/ScoreTable/5f60e245a28f9a78e6c07322.json" \ ``` </Step> <Step title="3. As a result, the API returns an updated array."> The 1st element of the `movie` array is updated to `Titanic`. ```json theme={null} { "_id": "5f60e245a28f9a78e6c07322", "_parent_id": null, "created_at": 1600184901, "movie": [ "Star Wars", "Titanic" ], "updated_at": 1600184901, "user_id": 102433734, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> <Accordion title="Update record with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "game_mode_name":"rainbow dash", }' \ "https://api.quickblox.com/data/ScoreTable/5f60e245a28f9a78e6c07322.json" \ ``` </Step> <Step title="2. Set a new value for a field"> You can update a value of the field. Here, the `game_mode_name` field is set to `rainbow dash`. ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "game_mode_name":"rainbow dash", }' \ "https://api.quickblox.com/data/ScoreTable/5f60e245a28f9a78e6c07322.json" \ ``` </Step> <Step title="3. As a result, the API returns an updated record."> ```json theme={null} { "_id": "5f60e245a28f9a78e6c07322", "_parent_id": null, "created_at": 1600184901, "expert_mode": null, "game_mode_name": "rainbow dash", "name": null, "progress": 0, "score_value": null, "updated_at": 1600184901, "user_id": 102433734, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </Step> </Steps> </Accordion> </AccordionGroup> To update the existing record you should know the record ID and use a `PUT` request with `application/json` or `application/x-www-form-urlencoded` content type of PUT/POST body. Received values will be casted according to the class defined in QuickBlox Dashboard. A field that is present in class but **not** specified in the PUT request has a `null` value. To nullify the existing value you should specify `null` for `application/x-www-/form-urlencoded`, and `null` for `application/json` content type. An update request updates specified fields only (other fields will be left untouched). For numeric fields `Integer` and `Float`, there is a special increment operator `inc` that increments or decrements the numeric field. **Update operators** The request can contain next update operators: | Operator | Description | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | inc | Types: integer, float <br /> <br />Example: <br />inc\[field1]=1000 <br /> <br />Increment field `<field_name>` to a specified value. The value can be positive or negative(decrement operation). | | pull | Types: arrays <br /> <br />Example: <br />pull\[field1]=val1 <br /> <br />Removes a specified value from the array field. | | pull with filter | Types: arrays <br /> <br />Example: <br />pull\[field1]\[\<filter\_operator>]=\<filter\_value> <br /> <br />Removes **all** elements filtered by filter operator from the array. | | pull\_all | Types: arrays <br /> <br />Example: <br />pull\_all\[field1]\[]=val1\&pull\_all\[field1]\[]=val2 <br /> <br />Removes **all** specified values from the array. | | pop | Types: arrays <br /> <br />Example: <br />pop\[field1]=1 <br /> <br />Removes the last element from the array. To remove the first, the element value should be equal to 1. | | push | Types: arrays <br /> <br />Example: <br />push\[field1]\[]=val1\&push\[field1]\[]=val2 <br /> <br />Appends specified values to the array. | | add\_to\_set | Types: arrays <br /> <br />Example: <br />add\_to\_set\[\<field\_name>]= <br /> <br />Adds a value to the array only if the value is **not** in the array already. | | Update array element by index operator | Type: arrays <br /> <br />Example: <br />\<field\_name>\[\<index>]=val <br /> <br />Update the array element by index. | **Search operators** The request can contain all, some or none of the next search operators: | Operator | Description | | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | lt | **Less Than** operator <br /> <br />Types:integer, float <br /> <br />Example: <br />score\_value\[lt]=1000 | | lte | **Less Than or Equal to** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />score\_value\[lte]=850 | | gt | **Greater Than** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />bonus\_count\[gt]=2.45 | | gte | **Greater Than or Equal to** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />bonus\_count\[gte]=56.443 | | ne | **Not Equal to** operator <br /> <br />Types: integer, float, string, boolean <br /> <br />Example: <br />game\_mode\_name\[ne]=ctf | | in | **Contained IN array** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />game\_mode\_name\[in]=deathmatch,rage | | nin | **Not contained IN array** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />game\_mode\_name\[nin]=survivor,crazy\_nightmare | | all | **ALL contained IN array** operator <br /> <br />Types: array <br /> <br />Example: <br />game\_modes\[all]=survivo,crazy | | or | **OR** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />name\[or]=sam,tim <br />name\[or]=sam\&lastname\[or]=johnson <br /> <br />Will return records with name `sam` **or** `tim`. <br /> <br />Will return records with name `sam` **or** last name `johnson`. | | ctn | **Contains** substring operator <br /> <br />Types: string <br /> <br />Example: <br />username\[ctn]=son <br /> <br />Will return all records where `username` field **contains** `son` substring. | | near | Types: location <br /> <br />Example: <br />mylocation\[near]=25.32,44.551;1000 <br /> <br />Search records in a specific radius with the current position in meters. Format: `{field_name}[near]=longitude,latitude;radius`. | **Allowed permissions** | Permission | Syntax | Example | | ------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Open | permissions.\<CRUD\_operation>.access | permissions.read.access=open | | Owner | permissions.\<CRUD\_operation>.access | permissions.read.access=owner | | Open for users IDs | permissions.\<CRUD\_operation>.access=open\_for\_users\_ids permissions.\<CRUD\_operation>.ids=id\_1,id\_2,id\_3,… | permissions.update.access=open\_for\_users\_ids permissions.update.ids=3,12 | | Open for groups | permissions.\<CRUD\_operation>.access=open\_for\_groups permissions.\<CRUD\_operation>.groups=group\_name\_1,group\_name\_2 | permissions.delete.access=open\_for\_groups permissions.delete.groups=experience,rate | #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> <ParamField type="string"> ID of the custom object record. Generated automatically by the server after record creation. </ParamField> #### Body Parameters <ParamField type="string"> Update a value to the field defined in Custom Object class. </ParamField> <ParamField type="string"> Update the array element by index. [Update Operators](#section-update-operators). Example: `cartoons[1]=Aladdin`. </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="integer"> Increment field. [Update Operators](#section-update-operators). </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="propeties"> <ParamField type="string"> Removes a specified value from the array field. [Update Operators](#section-update-operators). </ParamField> <ParamField type="object"> <Expandable title="{custom_array_field_M} object"> <ParamField type="string"> Removes all elements filtered by search operator from the array. [Search Operators](#section-search-operators). </ParamField> </Expandable> </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> Removes all specified values from the array. [Update Operators](#section-update-operators). </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> Removes the last element from the array. [Update Operators](#section-update-operators). </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> Appends specified values to the array. [Update Operators](#section-update-operators). </ParamField> </Expandable> </ParamField> <ParamField type="object"> <Expandable title="properties"> <ParamField type="string"> Adds a value to the array only if the value is not in the array already. [Update Operators](#section-update-operators). </ParamField> </Expandable> </ParamField> <ParamField type="string"> Record permissions. Format: <br /> `permission.<CRUD_operation>.access=<value>` <br /> `permission.<CRUD_operation>.<option>=<value>` CRUD operations: `create`, `read`, `update`, `delete`. <br /> CRUD access values: `open`, `owner`, `open_for_users_ids`, `open_for_groups`. <br /> CRUD options: `ids`, `groups`. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="_id" type="string" /> <ResponseField name="_parent_id" type="string" /> <ResponseField name="created_at" type="integer" /> <ResponseField name="updated_at" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="permissions" type="object"> <Expandable title="properties"> <ResponseField name="read" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="update" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="delete" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-token: c8d706877dc40c4f56f8be618dda898456013152" \ -d '{ "game_mode_name":"rainbow dash", }' \ https://api.quickblox.com/data/ScoreTable/5f60e245a28f9a78e6c07322.json \ ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "_id": "5f60e245a28f9a78e6c07322", "_parent_id": null, "created_at": 1600184901, "expert_mode": null, "game_mode_name": "rainbow dash", "name": null, "progress": 0, "score_value": null, "updated_at": 1600184901, "user_id": 102433734, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ``` </ResponseExample> # Update Records by Criteria Source: https://docs.quickblox.com/reference/update-records-by-criteria PUT https://api.quickblox.com/data/{class_name}/by_criteria.json Update existing records by criteria. #### Recipes <AccordionGroup> <Accordion title="Update records by criteria with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -d '{ "game_mode_name":"beta testing", "search_criteria":{ "expert_mode":"false", "progress":{ "gt":"0.2" } } }' \ https://api.quickblox.com/data/ScoreTable/by_criteria.json ``` </Step> <Step title="2. As a result, the API returns the number of updated records."> ```json theme={null} { "class_name": "ScoreTable", "skip": 0, "limit": 100, "total_found": 2, "items": [ { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.23, "score_value": 705, "updated_at": 1569177307, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } }, { "_id": "5d867291a0eb473ed96099c4", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4, 5, 6 ], "created_at": 1569092241, "date": "2019-09-21T21:25:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.42, "score_value": 3021, "updated_at": 1569177307, "user_avatar": null, "user_id": 96753878, "user_location": null, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ] } ``` </Step> </Steps> </Accordion> </AccordionGroup> You can update records by criteria using the `search_criteria` operator. **All** parameters that are not included in the search criteria will be considered as parameters for updating the fields of records found by the search criteria. You can use all search operators listed below the request. **Search operators** The request can contain all, some or none of the next search operators: | Operator | Description | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | lt | **Less Than** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />score\_value\[lt]=1000 | | lte | **Less Than or Equal to** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />score\_value\[lte]=850 | | gt | **Greater Than** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />bonus\_count\[gt]=2.45 | | gte | **Greater Than or Equal to** operator <br /> <br />Types: integer, float <br /> <br />Example: <br />bonus\_count\[gte]=56.443 | | ne | **Not Equal to** operator <br /> <br />Types: integer, float, string, boolean <br /> <br />Example: <br />game\_mode\_name\[ne]=ctf | | in | **Contained IN array** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />game\_mode\_name\[in]=deathmatch,rage | | nin | **Not contained IN array** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />game\_mode\_name\[nin]=survivor,crazy\_nightmare | | all | **ALL contained IN array** operator <br /> <br />Types: array <br /> <br />Example: <br />game\_modes\[all]=survivor,crazy | | or | **OR** operator <br /> <br />Types: integer, float, string <br /> <br />Example: <br />name\[or]=sam,tim <br />name\[or]=sam\&lastname\[or]=johnson <br /> <br />Will return records with name `sam` **or** `tim`. <br />Will return records with name `sam` **or** last name `johnson`. | | ctn | **Contains** substring operator <br /> <br />Types: string <br /> <br />Example: <br />username\[ctn]=son <br /> <br />Will return all records where `username` field **contains** `son` substring. | | near | Types: location <br /> <br />Example: <br />mylocation\[near]=25.32,44.551;1000 <br /> <br />Search records in a specific radius with <br />the current position in meters. <br />Format: `{field_name}[near]=longitude,latitude;radius`. | **Allowed permissions** | Permission | Syntax | Example | | ------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Open | permissions.\<CRUD\_operation>.access | permissions.read.access=open | | Owner | permissions.\<CRUD\_operation>.access | permissions.read.access=owner | | Open for users IDs | permissions.\<CRUD\_operation>.access=open\_for\_users\_ids permissions.\<CRUD\_operation>.ids=id\_1,id\_2,id\_3,… | permissions.update.access=open\_for\_users\_ids permissions.update.ids=3,12 | | Open for groups | permissions.\<CRUD\_operation>.access=open\_for\_groups permissions.\<CRUD\_operation>.groups=group\_name\_1,group\_name\_2 | permissions.delete.access=open\_for\_groups permissions.delete.groups=experience,rate | #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> #### Body Parameters <ParamField type="string"> Update value to the field defined in Custom Object class. </ParamField> <ParamField type="object"> <Expandable title="Search criteria object"> <ParamField type="string"> Search records with the field that contains the specified value. </ParamField> <ParamField type="object"> <Expandable title="{custom_field_M} object"> <ParamField type="string"> Search a record with the field that contains a value according to the specified value and operator. </ParamField> </Expandable> </ParamField> </Expandable> </ParamField> <ParamField type="string"> Record permissions. Format: <br /> `permission.<CRUD_operation>.access=<value>` <br /> `permission.<CRUD_operation>.<option>=<value>` CRUD operations: `create`, `read`, `update`, `delete`. <br /> CRUD access values: `open`, `owner`, `open_for_users_ids`, `open_for_groups`. <br /> CRUD options: `ids`, `groups`. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="class_name" type="string" /> <ResponseField name="skip" type="integer" /> <ResponseField name="limit" type="integer" /> <ResponseField name="total_found" type="integer" /> <ResponseField name="items" type="array of objects"> <Expandable title="properties"> <ResponseField name="_id" type="string" /> <ResponseField name="_parent_id" type="string" /> <ResponseField name="created_at" type="integer" /> <ResponseField name="updated_at" type="integer" /> <ResponseField name="user_id" type="integer" /> <ResponseField name="permissions" type="object"> <Expandable title="properties"> <ResponseField name="read" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="update" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> <ResponseField name="delete" type="object"> <Expandable title="properties"> <ResponseField name="access" type="string" /> </Expandable> </ResponseField> </Expandable> </ResponseField> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 0bea0abd041b2431e5e1c2d0b06fab4414012bba" \ -d '{ "game_mode_name":"beta testing", "search_criteria":{ "expert_mode":"false", "progress":{ "gt":"0.2" } } }' \ https://api.quickblox.com/data/ScoreTable/by_criteria.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "class_name": "ScoreTable", "skip": 0, "limit": 100, "total_found": 2, "items": [ { "_id": "5d866b53a28f9a5ad51cb562", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4 ], "created_at": 1569090387, "date": "2019-09-21T21:21:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.23, "score_value": 705, "updated_at": 1569177307, "user_avatar": null, "user_id": 96753878, "user_location": [ 11.13, 45.52 ], "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } }, { "_id": "5d867291a0eb473ed96099c4", "_parent_id": null, "completed_levels": [ 1, 2, 3, 4, 5, 6 ], "created_at": 1569092241, "date": "2019-09-21T21:25:00Z", "expert_mode": false, "game_mode_name": "beta testing", "progress": 0.42, "score_value": 3021, "updated_at": 1569177307, "user_avatar": null, "user_id": 96753878, "user_location": null, "permissions": { "read": { "access": "open" }, "update": { "access": "owner" }, "delete": { "access": "owner" } } } ] } ``` ```json 400 theme={null} {} ``` </ResponseExample> # Update User Source: https://docs.quickblox.com/reference/update-user PUT https://api.quickblox.com/users/{user_id}.json Update a user by ID. A user can be updated only by themselves or an account owner. You can use this request to update a user password. In this case, you need to pass the old password and new password to the update user request. #### Recipes <AccordionGroup> <Accordion title="Update user password"> <Steps> <Step title="1. Use a `user` parameter to set a new password"> ```curl {5} theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H "QB-Token: 7483d80bb9caddd504764d85b050c85755aecc84" \ -d '{ "user":{ "old_password":"myoldpassword!" \ "password":"mynewpassword!" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="2. Set an old password"> Use an `old_password` parameter to set an old password. ```curl {6} theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H "QB-Token: 7483d80bb9caddd504764d85b050c85755aecc84" \ -d '{ "user":{ "old_password":"myoldpassword!" \ "password":"mynewpassword!" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="3. Set a new password"> Use a `password` parameter to set a new password. ```curl {7} theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H "QB-Token: 7483d80bb9caddd504764d85b050c85755aecc84" \ -d '{ "user":{ "old_password":"myoldpassword!" \ "password":"mynewpassword!" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="4. As a result, the API returns a user with an updated password."> ```curl theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H "QB-Token: 7483d80bb9caddd504764d85b050c85755aecc84" \ -d '{ "user":{ "old_password":"myoldpassword!" \ "password":"mynewpassword!" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> </Steps> </Accordion> <Accordion title="Update user"> <Steps> <Step title="1. Use a `user` parameter to update a user"> ```curl {5} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 7483d80bb9caddd504764d85b050c85755aecc84" \ -d '{ "user":{ "login":"Jen_Lesser", "email":"jen_lesser@gmail.com", "external_user_id":"1433712", "facebook_id":"125553", "full_name":"Jen Lesser", "phone":"+965234215", "website":"https://jen.com", "tag_list":"android,web", "custom_data":"Head of marketing" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="2. Set new values for the fields"> You can update any field of the user. ```curl {6-14} theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 7483d80bb9caddd504764d85b050c85755aecc84" \ -d '{ "user":{ "login":"Jen_Lesser", "email":"jen_lesser@gmail.com", "external_user_id":"1433712", "facebook_id":"125553", "full_name":"Jen Lesser", "phone":"+965234215", "website":"https://jen.com", "tag_list":"android,web", "custom_data":"Head of marketing" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="3. As a result, the API returns a user with the specified updated fields."> ```curl theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 7483d80bb9caddd504764d85b050c85755aecc84" \ -d '{ "user":{ "login":"Jen_Lesser", "email":"jen_lesser@gmail.com", "external_user_id":"1433712", "facebook_id":"125553", "full_name":"Jen Lesser", "phone":"+965234215", "website":"https://jen.com", "tag_list":"android,web", "custom_data":"Head of marketing" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> </Steps> </Accordion> <Accordion title="Update user with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user":{ "login":"Jen_Lesser", "email":"jen_lesser@gmail.com", "external_user_id":"1433712", "facebook_id":"125553", "full_name":"Jen Lesser", "phone":"+965234215", "website":"https://jen.com", "tag_list":"android,web", "custom_data":"Head of marketing" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="2. Use a `user` parameter to update a user"> ```curl {5} theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user":{ "login":"Jen_Lesser", "email":"jen_lesser@gmail.com", "external_user_id":"1433712", "facebook_id":"125553", "full_name":"Jen Lesser", "phone":"+965234215", "website":"https://jen.com", "tag_list":"android,web", "custom_data":"Head of marketing" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="3. Set new values for the fields"> You can update any field of the user. ```curl {6-14} theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user":{ "login":"Jen_Lesser", "email":"jen_lesser@gmail.com", "external_user_id":"1433712", "facebook_id":"125553", "full_name":"Jen Lesser", "phone":"+965234215", "website":"https://jen.com", "tag_list":"android,web", "custom_data":"Head of marketing" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="4. As a result, the API returns a user with the specified updated fields."> ```curl theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user":{ "login":"Jen_Lesser", "email":"jen_lesser@gmail.com", "external_user_id":"1433712", "facebook_id":"125553", "full_name":"Jen Lesser", "phone":"+965234215", "website":"https://jen.com", "tag_list":"android,web", "custom_data":"Head of marketing" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> </Steps> </Accordion> <Accordion title="Update user password with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {3} theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user":{ "password":"newpasswordforuser!" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="2. Use a `user` parameter to set a new password"> ```curl {5} theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user":{ "password":"newpasswordforuser!" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="3. Set a new password"> Use a `password` parameter to set a new password for the specified user ```curl {6} theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user":{ "password":"newpasswordforuser!" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> <Step title="4. As a result, the API returns a user with an updated password."> ```curl theme={null} curl -X PUT\ -H "Content-Type: application/json" \ -H 'Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI' \ -d '{ "user":{ "password":"newpasswordforuser!" } }' \ https://api.quickblox.com/users/96831397.json ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="integer"> ID of the user. Generated automatically by the server after user creation.</ParamField> #### Body Parameters <ParamField type="object"> <Expandable title="User object properties"> <ParamField type="string"> User login. Must be **unique**. Only **one** required: `login` or `email`. </ParamField> <ParamField type="string"> User e-mail. Must be **unique**. Only **one** required: `login` or `email`. </ParamField> <ParamField type="string"> User password. </ParamField> <ParamField type="integer"> ID of the file/blob. Generated automatically by the server after file/blob creation (for example, user photo). </ParamField> <ParamField type="integer"> ID of the user in the external system (<Tooltip>External User ID</Tooltip>.). Must be **unique**. </ParamField> <ParamField type="string"> ID of the user's Facebook account. Must be **unique**. </ParamField> <ParamField type="string"> User's full name. </ParamField> <ParamField type="string"> User's phone. </ParamField> <ParamField type="string"> User's website. </ParamField> <ParamField type="string"> User's additional info. </ParamField> <ParamField type="string"> User tags. </ParamField> <ParamField type="boolean"> ~~Marketing info.~~ </ParamField> <ParamField type="boolean"> ~~Marketing info.~~ </ParamField> <ParamField type="boolean"> ~~GDPR info.~~ </ParamField> <ParamField type="string"> ~~GDPR info.~~</ParamField> </Expandable> </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization.</ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="user" type="object"> <Expandable title="properties"> <ResponseField name="id" type="integer" /> <ResponseField name="full_name" type="string" /> <ResponseField name="email" type="string" /> <ResponseField name="login" type="string" /> <ResponseField name="phone" type="string" /> <ResponseField name="website" type="string" /> <ResponseField name="created_at" type="string" /> <ResponseField name="updated_at" type="string" /> <ResponseField name="last_request_at" type="string" /> <ResponseField name="external_user_id" type="integer" /> <ResponseField name="facebook_id" type="string" /> <ResponseField name="twitter_id" type="string" /> <ResponseField name="blob_id" type="string" /> <ResponseField name="custom_data" type="string" /> <ResponseField name="age_over16" type="boolean" /> <ResponseField name="allow_statistics_analysis" type="boolean" /> <ResponseField name="allow_sales_activities" type="boolean" /> <ResponseField name="parents_contacts" type="string" /> <ResponseField name="user_tags" type="string" /> </Expandable> </ResponseField> </Accordion> <Accordion title="400"> An error response <ResponseField name="code" type="string" /> <ResponseField name="message" type="string" /> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> </Expandable> </ResponseField> </Accordion> <Accordion title="422"> An error response <ResponseField name="errors" type="object"> <Expandable title="properties"> <ResponseField name="base" type="array of strings" /> <ResponseField name="email" type="array of strings" /> <ResponseField name="login" type="array of strings" /> <ResponseField name="facebook_id" type="array of strings" /> <ResponseField name="twitter_id" type="array of strings" /> <ResponseField name="tag" type="array of strings" /> <ResponseField name="phone" type="array of strings" /> </Expandable> </ResponseField> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X PUT \ -H "Content-Type: application/json" \ -H "QB-Token: 7483d80bb9caddd504764d85b050c85755aecc84" \ -d '{ "user":{ "login":"Jen_Lesser", "email":"jen_lesser@gmail.com", "external_user_id":"1433712", "facebook_id":"125553", "full_name":"Jen Lesser", "phone":"+965234215", "website":"https://jen.com", "tag_list":"android,web", "custom_data":"Head of marketing" } }' \ https://api.quickblox.com/users/96831397.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "user": { "id": 96831397, "full_name": "Jen Lesser", "email": "jen_lesser@gmail.com", "login": "Jen_Lesser", "phone": "+965234215", "website": "https://jen.com", "created_at": "2019-08-29T19:15:17Z", "updated_at": "2019-09-01T14:03:58Z", "last_request_at": "2019-09-01T14:02:54Z", "external_user_id": 1433712, "facebook_id": "125553", "twitter_id": "998800", "blob_id": null, "custom_data": "Head of marketing", "age_over16": true, "allow_statistics_analysis": false, "allow_sales_activities": false, "parents_contacts": "", "user_tags": "android,web" } } ``` ```json 400 theme={null} { "code": null, "message": "No data was provided" } ``` ```json 403 theme={null} { "errors": { "base": [ "You are not owner of this object" ] } } ``` ```json 422 theme={null} // Validation errors { "errors": { "email": [ "has already been taken." ], "login": [ "has already been taken" ], "facebook_id": [ "has already been taken" ], "twitter_id": [ "has already been taken" ], "tag": [ "length should between 3..15 symbols", "should contain alphanumeric characters and start with a letter" ], "phone": [ "is not a number" ] } } // Incorrect old password { "errors": { "base": [ "Incorrect old password provided" ] } } ``` </ResponseExample> # Upload Address Book Source: https://docs.quickblox.com/reference/upload-address-book POST https://api.quickblox.com/address_book.json Upload address book contacts. #### Recipes <AccordionGroup> <Accordion title="Delete contact"> <Steps> <Step title="1. Use a `contacts` parameter to set an array of contact hashes"> ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Ann", "phone": "11223334237", "destroy": 1 } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="2. Set a name of the contact"> Note that a name is limited to a number of signs. Min: **1**. Max: **25**. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Ann", "phone": "11223334237", "destroy": 1 } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="3. Set a phone of the contact"> Note that a phone is limited to a number of signs. Min: **10**. Max: **15**. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Ann", "phone": "11223334237", "destroy": 1 } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="4. Specify if you want to delete a contact"> Set a `destroy` parameter as `1` to delete a contact. ```curl {9} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Ann", "phone": "11223334237", "destroy": 1 } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="5. Set a device identifier"> Set a `udid` parameter to specify a user's device. If specified, a contact will be deleted for a specified device only. ```curl {12} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Ann", "phone": "11223334237", "destroy": 1 } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="6. As a result, the API returns a response specifying that a contact was deleted."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Ann", "phone": "11223334237", "destroy": 1 } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> </Steps> </Accordion> <Accordion title="Upload address book with incorrect data"> <Steps> <Step title="1. Use a `contacts` parameter to set an array of contact hashes"> ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Alex" }, { "phone": "45645645666" }, { "name": "", "phone": "456456456" } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="2. Set a `name` and `phone` of the contact as separate arrays"> Note that it's incorrect to set a `name` separately from a `phone`. ```curl {7, 10} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Alex" }, { "phone": "45645645666" }, { "name": "", "phone": "456456456" } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="3. Set a `name` as an empty string"> Note that a name is limited to a number of signs. Min: **1**. Max: **255**. ```curl {13} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Alex" }, { "phone": "45645645666" }, { "name": "", "phone": "456456456" } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="4. Set an incorrect phone length"> Note that a phone is limited to a number of signs. Min: **10**. Max: **15**. ```curl {14} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Alex" }, { "phone": "45645645666" }, { "name": "", "phone": "456456456" } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="5. As a result, the API returns a `200` status response specifying that the data were set incorreclty."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Alex" }, { "phone": "45645645666" }, { "name": "", "phone": "456456456" } ], "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> </Steps> </Accordion> <Accordion title="Replace contacts"> <Steps> <Step title="1. Use a `contacts` parameter to set an array of contact hashes"> ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Howard", "phone": "12312312333" } ], "force":1, "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="2. Set a name of a contact"> Note that a `name` is limited to a number of signs. Min: **1**. Max: **255**. ```curl {7} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Howard", "phone": "12312312333" } ], "force":1, "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="3. Set a phone of a contact"> Note that a `phone` is limited to a number of signs. Min: **10**. Max: **15**. ```curl {8} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Howard", "phone": "12312312333" } ], "force":1, "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="4. Specify if you want to replace existing contacts with the one specified"> Set a `force` as `1` to replace the contacts. ```curl {11} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Howard", "phone": "12312312333" } ], "force":1, "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="5. Set a device identifier"> Set a `udid` parameter to specify a user's device. If specified, contacts will be replaced for a specified device only. ```curl {12} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Howard", "phone": "12312312333" } ], "force":1, "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="6. As a result, the API returns a responce specifying that `1` contact was created and `3` deleted."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Howard", "phone": "12312312333" } ], "force":1, "udid": "2222E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> </Steps> </Accordion> <Accordion title="Upload address book"> <Steps> <Step title="1. Use a `contacts` parameter to set an array of contact hashes"> ```curl {5} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Mike Howard", "phone": "463571393241" }, { "name": "Morgan Fuller", "phone": "65021272571" } ], "udid": "A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="2. Set a name of the contact"> Use a `name` parameter to set the name of the contact. ```curl {7, 11} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Mike Howard", "phone": "463571393241" }, { "name": "Morgan Fuller", "phone": "65021272571" } ], "udid": "A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="3. Set a phone of the contact"> Use a `phone` parameter to set a phone of the contact. ```curl {8, 12} theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Mike Howard", "phone": "463571393241" }, { "name": "Morgan Fuller", "phone": "65021272571" } ], "udid": "A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> <Step title="4. As a result, the API returns a `200` status response specifying that the contacts were uploaded."> ```curl theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Mike Howard", "phone": "463571393241" }, { "name": "Morgan Fuller", "phone": "65021272571" } ], "udid": "A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </Step> </Steps> </Accordion> </AccordionGroup> This API allows you to do following things: * Upload new address book. * Replace old address book with a new one. * Add/delete individual contacts. * Update existing contacts. #### Body Parameters <ParamField type="array"> Contains an array of contact objects. Each contact can contain 3 keys - `phone`, `name`, and `destroy`. <Expandable title="object"> <ParamField type="string">Contact phone. Min: 10. Max: 15.</ParamField> <ParamField type="string">Contact name. Required only for create/update. Min: 1. Max: 255. </ParamField> <ParamField type="boolean"> Used in a case of a contact destroy. Set `destroy=1` to apply. </ParamField> </Expandable> </ParamField> <ParamField type="integer"> Defines force rewrite mode. Set `force=1` to apply, then all previous contacts for the device context will be replaced by new ones. </ParamField> <ParamField type="string"> User's device identifier. If specified, all operations will be in this context. Max. length is 64 symbols. If the device identifier is not specified, it means that a user has one global address book across all their devices. </ParamField> #### Headers <ParamField type="string"> A user session token. See [Authentication](/reference/authentication) page to learn more about the session token. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="created" type="integer" /> <ResponseField name="updated" type="integer" /> <ResponseField name="deleted" type="integer" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "Content-Type: application/json" \ -H "QB-Token: 6d59d901c1231a4d74572224b88ccbc487012bba" \ -d '{ "contacts": [ { "name": "Mike Howard", "phone": "463571393241" }, { "name": "Morgan Fuller", "phone": "65021272571" } ], "udid": "A337E8A4-80AD-8ABA-9F5D-579EFF6BACAB" }' \ https://api.quickblox.com/address_book.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "created": 2, "updated": 0, "deleted": 0 } ``` </ResponseExample> # Upload File Source: https://docs.quickblox.com/reference/upload-file POST https://api.quickblox.com/blobs/{blob_id}/upload.json Upload a local file to the QuickBlox cloud storage. The request is a multipart HTTP request where one part is a JSON object and the other part is the content of the file itself. To upload a file, proceed as following: 1. [Create File](/reference/create-file). Make sure to create an entity for a file within the system - a blob, **before** uploading it. 2. Upload File. An upload link is obtained from the response to the [create file](/reference/create-file) request. The link has expiration time so if it gets expired, you should obtain a new link using the create file request. **File size limit** The maximum size of the uploaded file depends on the membership plan. | | Basic | Startup | Growth | HIPAA | Enterprise | | --------------- | ----- | ------- | ------ | ----- | --------------------------------------------------------------- | | File size limit | 10 Mb | 25 Mb | 50Mb | 50Mb | [Contact our sales team](https://quickblox.com/enterprise/#get) | #### Path Parameters <ParamField type="integer"> ID of the file/blob. Generated automatically by the server after file/blob creation. </ParamField> #### Body Parameters <ParamField type="string"> A binary file to be uploaded to the cloud storage. The file size is restricted by the membership plan. See the [File size limit](/reference/content#file-size-limit) section below. </ParamField> <ParamField type="string"> File/blob identifier that should be taken from the response of the Create File request. This is the UID parameter. </ParamField> #### Responses <AccordionGroup> <Accordion title="201"> A successful response </Accordion> <Accordion title="400"> An error response </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST 'https://api.quickblox.com/blobs/10070416/upload/' \ -F 'key=20456a9676674f4ca950d963dfca630e00' \ -F 'file=@/Users/test/Downloads/33.jpg' ``` </RequestExample> <ResponseExample> ```json 201 theme={null} Empty body ``` ```json 400 theme={null} {} ``` </ResponseExample> # Upload/Update File Source: https://docs.quickblox.com/reference/upload-update-file POST https://api.quickblox.com/data/{class_name}/{custom_object_record_id}/file.json Upload/update the content of file fields. #### Recipes <AccordionGroup> <Accordion title="Upload/update file with API key"> <Steps> <Step title="1. Use 'Authorization' header to pass API key"> ```curl {2} theme={null} curl -X POST \ -H "Authorization: ApiKey 28irlNAGasWDSupO9Vw0BBMZfuHrAUYKpmroS9yBORI" \ -F "field_name=avatar" \ -F 'file=@"my_avatar.jpg"' \ https://api.quickblox.com/data/UserProfileClass/5d863a78a0eb474dae6099c6/file.json ``` </Step> <Step title="2. As a result, the file is uploaded and the API returns its metadata."> ```json theme={null} { "file_id": "5d863c18a28f9a3a551cb557", "size": 2004727, "name": "Image.jpg", "content_type": "image/jpeg" } ``` </Step> </Steps> </Accordion> </AccordionGroup> #### Path Parameters <ParamField type="string"> Custom object class name. </ParamField> <ParamField type="string"> ID of the custom object record. Generated automatically by the server after record creation. </ParamField> #### Body Parameters <ParamField type="string"> Custom object record field which contains a file. </ParamField> <ParamField type="file"> File for uploading. The max file size is 32 MB. </ParamField> #### Headers <ParamField type="string"> A user or application session token. See [Authentication](/reference/authentication) page to learn more about session tokens. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> API key from Dashboard. Expected format: `ApiKey {your_api_key}`. Must be used either QB-Token or Authorization. </ParamField> <ParamField type="string"> User ID. The user ID of the user on whose behalf the request is being made. </ParamField> #### Responses <AccordionGroup> <Accordion title="200"> A successful response <ResponseField name="file_id" type="string" /> <ResponseField name="size" type="integer" /> <ResponseField name="name" type="string" /> <ResponseField name="content_type" type="string" /> </Accordion> <Accordion title="403"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> <Accordion title="404"> An error response <ResponseField name="errors" type="array of strings" /> </Accordion> </AccordionGroup> <RequestExample> ```bash cURL theme={null} curl -X POST \ -H "QB-Token: a04ed6adfdb98cde99837088830349f67d012bba" \ -F "field_name=avatar" \ -F 'file=@"my_avatar.jpg"' \ https://api.quickblox.com/data/UserProfileClass/5d863a78a0eb474dae6099c6/file.json ``` </RequestExample> <ResponseExample> ```json 200 theme={null} { "file_id": "5d863c18a28f9a3a551cb557", "size": 2004727, "name": "Image.jpg", "content_type": "image/jpeg" } ``` ```json 403 theme={null} { "errors": [ "Undefined class" ] } ``` ```json 404 theme={null} { "errors": [ "Wrong arguments" ] } ``` </ResponseExample> # User Model Source: https://docs.quickblox.com/reference/user-model | Property name | Type | Description | | --------------------------- | --------- | ------------------------------------------------------------------------------------ | | id | int | ID of the user. Generated automatically by the server after user creation. | | full\_name | string | User's full name. | | email | string | User's email. | | login | string | User's login. | | phone | string | User's phone number. | | website | string | User's website url. | | created\_at | datetime | Date & time when record was created, filled automatically. | | updated\_at | datetime | Date & time when record was updated, filled automatically. | | last\_request\_at | timestamp | Date & time when a user sent the last request, filled automatically. | | external\_user\_id | int | ID of the user in the external system (External User ID). | | facebook\_id | string | ID of the user's Facebook account. | | twitter\_id | string | **(Deprecated)** ~~ID of user's Twitter account.~~ | | blob\_id | int | ID of the file/blob. Generated automatically by the server after file/blob creation. | | custom\_data | string | User's additional info. | | user\_tags | string | User's tags. Comma separated array of tags. | | allow\_sales\_activities | boolean | **(Deprecated)** ~~Marketing info.~~ | | allow\_statistics\_analysis | boolean | **(Deprecated)** ~~Marketing info.~~ | | age\_over16 | boolean | **(Deprecated)** ~~GDPR info.~~ | | parents\_contacts | string | **(Deprecated)** ~~GDPR info.~~ | # Users Source: https://docs.quickblox.com/reference/users The User module manages everything related to user accounts handling, authentication, account data, etc. * **OAuth** (open-standard authorization protocol) is secure and supports transparent authentication within a secure user session. * **Social login**. Logging in users with their Facebook/Firebase phone number accounts is available. * **User tags**. Set up user tags and address them separately in your app or through the Dashboard. Tags can be added/deleted in the user’s profile. * **Existing user base integration**. Our module supports connection to your system by storing your existent IDs table and matching them to new ones. # Address Book Source: https://docs.quickblox.com/sdks/android-address-book Learn how to store and sync the phone contact list with QuickBlox. Address Book API provides an interface to work with the phone address book. Upload it to the server and retrieve already registered QuickBlox users from your address book. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. ## Upload address book First of all, you need to upload your address book to the backend. It's a normal practice to do a full upload for the first time and then upload new contacts on future app logins. <Tabs> <Tab title="Java"> ```Java theme={null} ArrayList<QBAddressBookContact> contacts = new ArrayList<>(); QBAddressBookContact contact1 = new QBAddressBookContact(); contact1.setName("QuickBlox United States"); contact1.setPhone("14157558221"); contacts.add(contact1); QBAddressBookContact contact2 = new QBAddressBookContact(); contact2.setName("QuickBlox United Kingdom"); contact2.setPhone("442081337343"); contacts.add(contact2); QBAddressBookContact contact3 = new QBAddressBookContact(); contact3.setName("QuickBlox Ukraine"); contact3.setPhone("380955868675"); contacts.add(contact3); QBAddressBookContact contact4 = new QBAddressBookContact(); contact4.setName("QuickBlox India"); contact4.setPhone("916363113628"); contacts.add(contact4); String udid = null; boolean force = false; QBUsers.uploadAddressBook(contacts, udid, force).performAsync(new QBEntityCallback<QBAddressBookResponse>() { @Override public void onSuccess(QBAddressBookResponse addressBookResponse, Bundle bundle) { int created = addressBookResponse.getCreatedCount(); int deleted = addressBookResponse.getDeletedCount(); int updated = addressBookResponse.getUpdatedCount(); Map<String, List> rejected = addressBookResponse.getRejectedErrors(); } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val contacts = ArrayList<QBAddressBookContact>() val contact1 = QBAddressBookContact() contact1.name = "QuickBlox United States" contact1.phone = "14157558221" contacts.add(contact1) val contact2 = QBAddressBookContact() contact2.name = "QuickBlox United Kingdom" contact2.phone = "442081337343" contacts.add(contact2) val contact3 = QBAddressBookContact() contact3.name = "QuickBlox Ukraine" contact3.phone = "380955868675" contacts.add(contact3) val contact4 = QBAddressBookContact() contact4.name = "QuickBlox India" contact4.phone = "916363113628" contacts.add(contact4) val udid: String? = null val force = false QBUsers.uploadAddressBook(contacts, udid, force).performAsync(object : QBEntityCallback<QBAddressBookResponse>{ override fun onSuccess(addressBookResponse: QBAddressBookResponse?, bundle: Bundle?) { val created = addressBookResponse?.createdCount val deleted = addressBookResponse?.deletedCount val updated = addressBookResponse?.updatedCount val rejected = addressBookResponse?.rejectedErrors } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> * You also can edit an existing contact by providing a new name for it. * You also can upload more contacts, not just all in one request. They will be added to your address book on the backend. If you want to override the whole address book on the backend just provide `force = true` option. * A device `UDID` is a unique device identifier. The `UDID` is used in cases where a user has two or more devices and contacts sync is off. Otherwise, a user has a single global address book. The `UDID` maximum length is 64 symbols. <Note> - Add a phone number without the **+** sign to be able to receive a push notification when the user having this phone number becomes available in your application. - Phone number length should be from 10 to 15 digits. </Note> ## Retrieve address book You can retrieve your uploaded address book using the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} String udid = null; QBUsers.getAddressBook(udid).performAsync(new QBEntityCallback<ArrayList<QBAddressBookContact>>() { @Override public void onSuccess(ArrayList<QBAddressBookContact> addressBookContacts, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val udid: String? = null QBUsers.getAddressBook(null).performAsync(object : QBEntityCallback<ArrayList<QBAddressBookContact>>{ override fun onSuccess(addressBookContacts: ArrayList<QBAddressBookContact>?, b: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Update contacts If you need to update the name or phone number in your address book contacts, you should use the code sample below. <Tabs> <Tab title="Java"> ```Java theme={null} ArrayList<QBAddressBookContact> contactsToUpdate = new ArrayList<>(); QBAddressBookContact contactToUpdate = new QBAddressBookContact(); contactToUpdate.setPhone("380955868675"); // existing phone number contactToUpdate.setName("QuickBlox in Ukraine"); // name to replace in the Server contactsToUpdate.add(contactToUpdate); String udid = null; QBUsers.uploadAddressBook(contactsToUpdate, udid, false).performAsync(new QBEntityCallback<QBAddressBookResponse>() { @Override public void onSuccess(QBAddressBookResponse addressBookResponse, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val contactsToUpdate = ArrayList<QBAddressBookContact>() val contactToUpdate = QBAddressBookContact() contactToUpdate.phone = "380955868675" // existing phone number contactToUpdate.name = "QuickBlox in Ukraine" // name to replace in the Server contactsToUpdate.add(contactToUpdate) val udid: String? = null QBUsers.uploadAddressBook(contactsToUpdate, udid, false).performAsync(object : QBEntityCallback<QBAddressBookResponse>{ override fun onSuccess(addressBookResponse: QBAddressBookResponse?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Delete contacts You can delete contacts by using the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} ArrayList<QBAddressBookContact> contactsToDelete = new ArrayList<>(); QBAddressBookContact contactToDelete = new QBAddressBookContact(); contactToDelete.setPhone("380950906090"); // existing phone number contactToDelete.setIsDestroy(true); // to mark as Deleted contactsToDelete.add(contactToDelete); String udid = null; QBUsers.uploadAddressBook(contactsToDelete, udid, false).performAsync(new QBEntityCallback<QBAddressBookResponse>() { @Override public void onSuccess(QBAddressBookResponse addressBookResponse, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val contactsToDelete = ArrayList<QBAddressBookContact>() val contactToDelete = QBAddressBookContact() contactToDelete.phone = "380950906090" // existing phone number contactToDelete.isDestroy = true // to mark as Deleted contactsToDelete.add(contactToDelete) val udid: String? = null QBUsers.uploadAddressBook(contactsToDelete, udid, false).performAsync(object : QBEntityCallback<QBAddressBookResponse>{ override fun onSuccess(addressBookResponse: QBAddressBookResponse?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Retrieve registered users Using this request, you can easily retrieve the QuickBlox users - your address book contacts that are already registered in your app. Users are matched with address book contacts by phone number, so user and address book contact must have the same phone number to be included in response. <Tabs> <Tab title="Java"> ```Java theme={null} String udid = null; boolean isCompact = false; QBUsers.getRegisteredUsersFromAddressBook(udid, isCompact).performAsync(new QBEntityCallback<ArrayList<QBUser>>() { @Override public void onSuccess(ArrayList<QBUser> users, Bundle bundle) { // successfully Loaded registered contacts } @Override public void onError(QBResponseException exception) { // loading Error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val udid: String? = null val isCompact = false QBUsers.getRegisteredUsersFromAddressBook(udid, isCompact).performAsync(object : QBEntityCallback<ArrayList<QBUser>>{ override fun onSuccess(users: ArrayList<QBUser>?, bundle: Bundle?) { // successfully Loaded registered contacts } override fun onError(exception: QBResponseException?) { // loading Error } }) ``` </Tab> </Tabs> If the `isCompact ` parameter is `true`, the server will return **only** the `userId` and `phone` fields of the `QBUser` model. Otherwise, all fields of the `QBUser` model will be returned. ## Push notification on new contact joined There is a way to get a push notification when some contact from your Address Book has been registered in the app. You can enable this feature at QuickBlox Dashboard. Just follow **Dashboard => *YOUR\_APP* => Users => Settings** direction and enable push notifications for new contacts. <Frame> <img alt="android-users-settings.png" /> </Frame> # Authentication Source: https://docs.quickblox.com/sdks/android-authentication Learn how to authenticate your users with QuickBlox. Every user needs to authenticate with QuickBlox before using any QuickBlox functionality. When someone connects with an application using QuickBlox, the application needs to obtain a session token which provides temporary secure access to QuickBlox APIs. A session token is an opaque string that identifies a user and an application. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. ## Session token rights There are different types of session tokens to support different use cases. | Session Token Type | Description | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Application session token | This kind of access token is needed to read the app data. Has only READ access to resources. The expiration time after the last REST API request is 2 hours. | | User session token | The user token is the most commonly used type of token. This kind of access token is needed any time the app calls an API to read, modify or write a specific user's data on their behalf. Has READ/WRITE access to resources. The expiration time after the last REST API request is 2 hours. | ## Session management By default, when a session gets expired, a new session with a new session token is created automatically. Thus, QuickBlox SDK stores a session token and uses it for all subsequent requests within the current session. However, you can disable the automatic session creation. Use the `setAutoCreateSession()` method and pass `false` as its argument to disable the functionality. <Tabs> <Tab title="Java"> ```Java theme={null} QBSettings.getInstance().setAutoCreateSession(false); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBSettings.getInstance().isAutoCreateSession = false ``` </Tab> </Tabs> You can track session states using `QBSessionManager` class and `SessionListener` listener. <Tabs> <Tab title="Java"> ```Java theme={null} QBSessionManager.getInstance().addListener(new QBSessionManager.QBSessionListener() { @Override public void onSessionCreated(QBSession session) { // calls when session was created firstly or after it has been expired } @Override public void onSessionUpdated(QBSessionParameters sessionParameters) { // calls when user signed in or signed up // QBSessionParameters stores information about signed in user. } @Override public void onSessionDeleted() { // calls when user signed Out or session was deleted } @Override public void onSessionRestored(QBSession session) { // calls when session was restored from local storage } @Override public void onSessionExpired() { // calls when session is expired } @Override public void onProviderSessionExpired(String provider) { // calls when provider's access token is expired or invalid } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBSessionManager.getInstance().addListener(object : QBSessionManager.QBSessionListener { override fun onSessionCreated(session: QBSession) { // calls when session was created firstly or after it has been expired } override fun onSessionUpdated(sessionParameters: QBSessionParameters) { // calls when user signed in or signed up // QBSessionParameters stores information about signed in user. } override fun onSessionDeleted() { // calls when user signed Out or session was deleted } override fun onSessionRestored(session: QBSession) { // calls when session was restored from local storage } override fun onSessionExpired() { // calls when session is expired } override fun onProviderSessionExpired(provider: String) { // calls when provider's access token is expired or invalid } }) ``` </Tab> </Tabs> ## Get session At any time you can get details about your current session token. <Tabs> <Tab title="Java"> ```Java theme={null} QBSessionParameters sessionParameters = QBSessionManager.getInstance().getSessionParameters(); sessionParameters.getUserId(); sessionParameters.getUserLogin(); sessionParameters.getUserEmail(); sessionParameters.getSocialProvider(); sessionParameters.getAccessToken(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val sessionParameters = QBSessionManager.getInstance().getSessionParameters() sessionParameters.userId sessionParameters.userLogin sessionParameters.userEmail sessionParameters.socialProvider sessionParameters.accessToken ``` </Tab> </Tabs> Also, you can check whether you are logged in or not. <Tabs> <Tab title="Java"> ```Java theme={null} boolean isLoggedIn = QBSessionManager.getInstance().getSessionParameters() != null; ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val isLoggedIn = QBSessionManager.getInstance().sessionParameters != null ``` </Tab> </Tabs> ## Sign up user Before you log in the user, you must create the user on QuickBlox. Recommendations are below: 1. **For POCs/MVPs:** Create the user using the [QuickBlox Dashboard](https://admin.quickblox.com/) or in client app with application session token. 2. **For production apps:** Use the [QuickBlox Create User API](https://docs.quickblox.com/reference/create-user) with [API key](/docs/application#create-api-key) on your backend to create the user when your user signs up in your app. <Warning> Security It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. </Warning> You can create a user with application session token in client app by calling `signUp()` method. <Tabs> <Tab title="Java"> ```Java theme={null} final QBUser user = new QBUser(); user.setLogin("johnsmith"); user.setPassword("johnPassword"); QBUsers.signUp(user).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val user = QBUser() user.login = "johnsmith" user.password = "johnPassword" QBUsers.signUp(user).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Log in user QuickBlox provides four types of user authentication: login/email and password, social, phone number, and custom identity provider login. ### Login/email and password Log in a user just by using login (or email) and password. Other fields are optional. Thus, the QuickBlox server requests a user's database for a match. If there is a match, a user session is created. <Tabs> <Tab title="Java"> ```Java theme={null} final QBUser user = new QBUser(); user.setLogin("johnsmith"); user.setPassword("johnPassword"); QBUsers.signIn(user).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val user = QBUser() user.login = "johnsmith" user.password = "johnPassword" QBUsers.signIn(user).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ### Social Authenticate with QuickBlox using a social network access token. <Tabs> <Tab title="Java"> ```Java theme={null} String accessToken = "8b75a6c7191285499d890a81df4ee7fe49bc732a"; String accessTokenSecret = null; String provider = QBProvider.FACEBOOK; QBUsers.signInUsingSocialProvider(provider, accessToken, accessTokenSecret).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val accessToken = "Az9dgLfyK7tZBSAz9dgLfyK7tQNttIoaZA10niR68DO" val accessTokenSecret: String? = null val provider = QBProvider.FACEBOOK QBUsers.signInUsingSocialProvider(provider, accessToken, accessTokenSecret).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Parameters | Description | | ----------------- | ------------------------------------------------------------------------------------ | | provider | A social network provider. | | accessToken | An access token received from the social network after a user authenticates with it. | | accessTokenSecret | A social network provider's access token secret. | ### Phone number A sign-in with a phone number is supported with **Firebase integration**. In order to implement authentication via phone number functionality, follow this [Firebase document](https://firebase.google.com/docs/auth/android/phone-auth). Don't forget to enable phone number sign-in for your Firebase project. To learn how to do this, see this [Firebase document](https://firebase.google.com/docs/auth/android/phone-auth#before-you-begin). You also need to sign in the user to Firebase. You can sing in the user **only after** completing a series of steps that involve sending a verification code to the user's phone, entering the verification code that Firebase sent to the user's phone, creating a `PhoneAuthCredential` object, etc. Refer this [Firebase document](https://firebase.google.com/docs/auth/android/phone-auth#sign-in-the-user) for more details. To log in the user to QuickBlox, use the `signInUsingFirebase()` method. Pass the project ID and ID token to the `signInUsingFirebase()`. The ID token is received as a result of the `getIdToken()` method. <Tabs> <Tab title="Java"> ```Java theme={null} private void refreshFirebaseAccessToken() { FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser(); firebaseUser.getIdToken(false).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() { @Override public void onComplete(@NonNull Task<GetTokenResult> task) { if (task.isSuccessful()) { // getting access token - successful if (task.getResult() != null) { firebaseAccessToken = task.getResult().getToken(); // save access token } } else { // getting access token - unsuccessful } } }); } private void loginByPhone() { QBUsers.signInUsingFirebase(firebaseProjectID, firebaseAccessToken).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { // a user has successful signed In } @Override public void onError(QBResponseException exception) { // handle error } }); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} private fun refreshFirebaseAccessToken() { val firebaseUser = FirebaseAuth.getInstance().currentUser firebaseUser?.getIdToken(false)?.addOnCompleteListener { task -> if (task.isSuccessful) { // getting access token - successful if (task.result != null) { firebaseAccessToken = task.result!!.token!! // save access token } } else { // getting access token - unsuccessful } } } private fun loginByPhone() { QBUsers.signInUsingFirebase(firebaseProjectID, firebaseAccessToken).performAsync(object : QBEntityCallback<QBUser>{ override fun onSuccess(user: QBUser?, bundle: Bundle?) { // a user has successful signed In } override fun onError(exception: QBResponseException?) { // handle error } }) } ``` </Tab> </Tabs> Pass the following arguments to the `signInUsingFirebase()` method. | Arguments | Description | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | firebaseProjectID | Firebase project ID. When you create a Firebase project, Firebase automatically assigns a unique ID to the project, but you can edit it during the project setup. | | firebaseAccessToken | ID token of the Firebase user. Created by Firebase when a user signs in to an app. This token is received as a result of getIdToken() method. | ### Custom identity provider You can authenticate your application users from the external database with QuickBlox via Custom Identity Provider (CIdP). Just specify the user login and access token as a password to authenticate with QuickBlox. Review [Custom Identity Provider](/docs/custom-identity-provider) page for more details on the feature. <Tabs> <Tab title="Java"> ```Java theme={null} String userLogin = "johnsmith"; String identityToken = "Fd4kxd37z58dS4d2Ye7wh3"; QBUser user = new QBUser(); user.setLogin(userLogin); user.setPassword(identityToken); QBUsers.signIn(user).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val userLogin = "johnsmith" val identityToken = "Fd4kxd37z58dS4d2Ye7wh3" val user = QBUser() user.login = userLogin user.password = identityToken QBUsers.signIn(user).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> <Tip> This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). </Tip> ## Log out user If you have a user session, you can downgrade it to an application session by calling the `logout()` method. <Tabs> <Tab title="Java"> ```Java theme={null} QBUsers.signOut().performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBUsers.signOut().performAsync(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Destroy session token To destroy an application session use the following code. <Tabs> <Tab title="Java"> ```Java theme={null} QBAuth.deleteSession().performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBAuth.deleteSession().performAsync(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Set existing session Typically, a session token is stored in SDK after successful login and used for every subsequent API call. However, you may want to obtain and store the session on your server for better security. In this case, you can set [application or user token](https://docs.quickblox.com/reference/authentication#application-vs-user-session-token) into SDK using the `QBAuth.startSessionWithToken()` method, also before calling the method to get notified that the session has expired you need to add **QBSessionListenerImpl()** to **QBSessionManager**. Don't forget to \[log in user]\(/sdks/android-authentication #log-in-user) if you pass the application token. <Tabs> <Tab title="Java"> ```Java theme={null} QBSessionManager.getInstance().addListener(new QBSessionListenerImpl() { @Override public void onSessionExpired() { } }); String token = "3Fjs1su8ery463gjd8hf"; QBAuth.startSessionWithToken(token).performAsync(new QBEntityCallback<QBSession>() { @Override public void onSuccess(QBSession qbSession, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBSessionManager.getInstance().addListener(object : QBSessionListenerImpl() { override fun onSessionExpired() { } }) val token = "3Fjs1su8ery463gjd8hf" QBAuth.startSessionWithToken(token).performAsync(object : QBEntityCallback<QBSession>{ override fun onSuccess(qbSession: QBSession?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> <Tip> If you have version lower than 3.11.0, you can set the existing session token into SDK using the `createActiveSession()` method. </Tip> <Tabs> <Tab title="Java"> ```Java theme={null} String token = "3Fjs1su8ery463gjd8hf"; QBSessionManager.getInstance().createActiveSession(token, tokenExpirationDate); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val token = "3Fjs1su8ery463gjd8hf" QBSessionManager.getInstance().createActiveSession(token, tokenExpirationDate) ``` </Tab> </Tabs> # Connection Source: https://docs.quickblox.com/sdks/android-chat-connection Learn how to connect to the chat server and set connection settings. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Connect to chat server To connect to the chat server use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} final QBUser user = new QBUser(); user.setId(12345); user.setPassword("johnPassword"); QBChatService.getInstance().login(user, new QBEntityCallback() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val user = QBUser() user.id = 12315 user.password = "johnPassword" QBChatService.getInstance().login(user, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Connect to chat server with QuickBlox session token In case, you authenticate with QuickBlox via Firebase, Facebook, or Custom Identity Provider, you should connect to the chat server with QuickBlox user session token and QuickBlox user ID. As a result of successful authentication via Firebase, Facebook, or Custom Identity Provider, you receive a QuickBlox user session token and QuickBlox user ID. Use QuickBlox user session token as a password and QuickBlox user ID as a login to connect to the chat server. <Warning> Don't use the Firebase/Facebook access token as a password to connect to the chat server. You will receive the following runtime error in this case: ```xml theme={null} <failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> <not-authorized/> <text xml:lang='en'>Password not verified</text> </failure> ``` </Warning> <Tabs> <Tab title="Java"> ```Java theme={null} String token = QBSessionManager.getInstance().getToken(); int id = 47892; QBUser user = new QBUser(); user.setId(id); user.setPassword(token); QBChatService.getInstance().login(user, new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val token = QBSessionManager.getInstance().token val id = 47892 val user = QBUser() user.id = id user.password = token QBChatService.getInstance().login(user, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void, bundle: Bundle) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> The `login` method accepts a `user` argument with the following fields: | Fields | Required | Description | | ------ | -------- | ------------------------------------------------------------------- | | id | yes | The ID of a user. | | token | yes | Specifies an active QuickBlox user session token set as a password. | ## Subscribe to connection state Subscribe to the connection state changes using the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} ConnectionListener connectionListener = new ConnectionListener() { @Override public void connected(XMPPConnection connection) { } @Override public void authenticated(XMPPConnection connection, boolean resumed) { } @Override public void connectionClosed() { } @Override public void connectionClosedOnError(Exception exception) { } @Override public void reconnectionSuccessful() { } @Override public void reconnectingIn(int seconds) { } @Override public void reconnectionFailed(Exception exception) { } }; QBChatService.getInstance().addConnectionListener(connectionListener); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val connectionListener = object : ConnectionListener { override fun connected(connection: XMPPConnection) { } override fun authenticated(connection: XMPPConnection, resumed: Boolean) { } override fun connectionClosed() { } override fun connectionClosedOnError(exception: Exception) { } override fun reconnectionSuccessful() { } override fun reconnectingIn(seconds: Int) { } override fun reconnectionFailed(exception: Exception) { } } QBChatService.getInstance().addConnectionListener(connectionListener) ``` </Tab> </Tabs> ## Check if connected to chat server Check the connection state using the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} boolean isLoggedIn = chatService.isLoggedIn(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val isLoggedIn = chatService.isLoggedIn ``` </Tab> </Tabs> ## Disconnect from chat server Disconnect from the chat server using the snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} final QBChatService chatService = QBChatService.getInstance(); boolean isLoggedIn = chatService.isLoggedIn(); if (!isLoggedIn) { return; } chatService.logout(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { chatService.destroy(); } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val chatService = QBChatService.getInstance() val isLoggedIn = chatService.isLoggedIn if (!isLoggedIn) { return } chatService.logout(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { chatService.destroy() } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Enable auto-reconnect to chat The SDK reconnects automatically when the connection to the chat server is lost. There is a way to disable it and then manage it manually. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatService.getInstance().setReconnectionAllowed(true); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBChatService.getInstance().isReconnectionAllowed = true ``` </Tab> </Tabs> ## Manage chat connections To provide a seamless chat experience, our SDK manages connections to the chat server at an application-wide level. Thus, when your application goes background, you should disconnect from the chat server to be able to receive [offline messages](/sdks/android-chat-offline-messaging). When your application goes foreground, you should connect to the chat server to become online and start sending and receiving messages. Use `androidx.lifecycle.LifecycleObserver` to determine when the application goes background. <Tabs> <Tab title="Java"> ```Java theme={null} public class BackgroundListener implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) void onBackground() { QBChatService.getInstance().logout(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { QBChatService.getInstance().destroy(); } @Override public void onError(QBResponseException exception) { } }); } } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} class BackgroundListener : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) internal fun onBackground() { QBChatService.getInstance().logout(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { QBChatService.getInstance().destroy() } override fun onError(exception: QBResponseException?) { } }) } } ``` </Tab> </Tabs> Make sure to add an instance of the created lifecycle observer class to the `androidx.lifecycle.ProcessLifecycleOwner()` to process lifecycle changes within your app. <Tabs> <Tab title="Java"> ```Java theme={null} public class App extends Application { @Override public void onCreate() { super.onCreate(); ProcessLifecycleOwner.get().getLifecycle().addObserver(new BackgroundListener()); } } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} class App : Application() { override fun onCreate() { super.onCreate() ProcessLifecycleOwner.get().lifecycle.addObserver(BackgroundListener()) } } ``` </Tab> </Tabs> ## Set connection settings Use the settings below to configure the connection to the chat server. <Tabs> <Tab title="Java"> ```Java theme={null} // Chat connection configuration QBChatService.ConfigurationBuilder configurationBuilder = new QBChatService.ConfigurationBuilder(); configurationBuilder.setSocketTimeout(300); configurationBuilder.setUseTls(true); configurationBuilder.setKeepAlive(true); configurationBuilder.setAutojoinEnabled(false); configurationBuilder.setAutoMarkDelivered(true); configurationBuilder.setAllowListenNetwork(true); configurationBuilder.setPort(5223); QBChatService.setConfigurationBuilder(configurationBuilder); QBChatService chatService = QBChatService.getInstance(); chatService.login(user, new QBEntityCallback() { @Override public void onSuccess(Object object, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // Chat connection configuration val configurationBuilder = QBChatService.ConfigurationBuilder() configurationBuilder.socketTimeout = 300 configurationBuilder.isUseTls = true configurationBuilder.isKeepAlive = true configurationBuilder.isAutojoinEnabled = false configurationBuilder.setAutoMarkDelivered(true) configurationBuilder.isReconnectionAllowed = true configurationBuilder.setAllowListenNetwork(true) configurationBuilder.port = 5223 QBChatService.setConfigurationBuilder(configurationBuilder) val chatService = QBChatService.getInstance() chatService.login(user, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Parameters | Description | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | setSocketTimeout | Chat socket read timeout in seconds. Default: **60**. | | setUseTls | TLS security mode used when making the connection. Default: **true** . | | setKeepAlive | Keep-alive option for a socket connection. Keep-alive is the option allowing to detect a stale connection. Default: **true**. | | setAutojoinEnabled | Automatically join dialogs loaded or created on the server. Default: **false**. | | setAutoMarkDelivered | Automatically mark the received messages as delivered. Default: **true** . | | setAllowListenNetwork | Allow SDK to listen to changes of network states. Default: **true**. | | setPort | Chat connection port number. Default: **5223**. | # Contact List Source: https://docs.quickblox.com/sdks/android-chat-contact-list Learn how to create, update, manage, and add users to your contact list. The Contact List API is rather straightforward. A user A sends a request to become "friends" with a user B. The user B accepts the friend request. And now the user A and B appear in each other roster. This feature is also useful to get online/offline statuses of the users from the user's Contact List. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/android-chat-connection) page to learn how to do it. ## Add listener To use a contact list, you have to obtain it and set all needed listeners. <Tabs> <Tab title="Java"> ```Java theme={null} QBRosterListener rosterListener = new QBRosterListener() { @Override public void entriesDeleted(Collection<Integer> userIds) { } @Override public void entriesAdded(Collection<Integer> userIds) { } @Override public void entriesUpdated(Collection<Integer> userIds) { } @Override public void presenceChanged(QBPresence presence) { } }; QBSubscriptionListener subscriptionListener = new QBSubscriptionListener() { @Override public void subscriptionRequested(int userId) { // subscription was requested by user with ID = userId } }; // you should do this after login (connect) to the Chat QBRoster contactsRoster = QBChatService.getInstance().getRoster(); contactsRoster.setSubscriptionMode(QBRoster.SubscriptionMode.mutual); contactsRoster.addSubscriptionListener(subscriptionListener); contactsRoster.addRosterListener(rosterListener); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val rosterListener = object : QBRosterListener { override fun entriesDeleted(collection: Collection<Int>?) { } override fun entriesAdded(collection: Collection<Int>?) { } override fun entriesUpdated(collection: Collection<Int>?) { } override fun presenceChanged(presence: QBPresence?) { } } val subscriptionListener = QBSubscriptionListener { userId: Int -> { // subscription was requested by user with ID = userId } } // you should do this after login (connect) to the Chat val contactsRoster = QBChatService.getInstance().roster contactsRoster.subscriptionMode = QBRoster.SubscriptionMode.mutual contactsRoster.addSubscriptionListener(subscriptionListener) contactsRoster.addRosterListener(rosterListener) ``` </Tab> </Tabs> The following table lists all supported events of the `QBRosterListener`. | Event | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | entriesDeleted() | A contact list item is removed from the contact list. | | entriesAdded() | A new contact list item is added to the contact list. | | entriesUpdated() | A contact list item is updated. For example, a user's name can be updated. | | presenceChanged() | A user presence is changed. Care should be taken when using the presence data delivered as part of this event. Specifically, when a user account is online with multiple devices, the UI should account for that. For example, say a user is online with their desktop computer and mobile phone. If the user logs out of the instant messaging client on their mobile phone, the user should not be shown in the contact list as offline since they're still available on another device. | <Tip> You should call `chatService.getRoster()` only after successful connecting to Chat. To learn how to it, review [this section](/sdks/ios-chat-connection#connect-to-chat-server). `QBRosterListener` is a listener that is fired any time a contact list is changed or the presence of a user in the contact list is changed (for example, user becomes online/offline). `QBSubscriptionListener` is a listener that is called `subscriptionRequested` when the current user has been requested for adding to the Contact List from any user. </Tip> ## Access contact list The following function gives you access to all contact list items. <Tabs> <Tab title="Java"> ```Java theme={null} Collection<QBRosterEntry> entries = contactsRoster.getEntries(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val entries = contactsRoster.entries ``` </Tab> </Tabs> A `QBRosterEntry` describes a user entity in your contact list. To get the Presence of a user, use `getPresence(userID)` method. Then, you can get a user's status. <Tabs> <Tab title="Java"> ```Java theme={null} QBPresence presence = contactsRoster.getPresence(user.getId()); if (presence == null) { // no user in your contact list return; } if (presence.getType() == QBPresence.Type.online) { // user is online } else { // user is offline } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val presence = contactsRoster.getPresence(user.id) if (presence == null) { // no user in your contact list return } if (presence.type == QBPresence.Type.online) { // user is online } else { // user is offline } ``` </Tab> </Tabs> ## Add user to your contact list To add a user to the contact list, use the following snippet. <Tabs> <Tab title="Java"> ```Java theme={null} try { contactsRoster.subscribe(user.getId()); } catch (SmackException.NotConnectedException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { contactsRoster.subscribe(user.id) } catch (exception: SmackException.NotConnectedException) { } ``` </Tab> </Tabs> After that, the User you have just subscribed to, receives a callback `subscriptionRequested` in `QBSubscriptionListener`. This is a request to be added to the contact list. <Tabs> <Tab title="Java"> ```Java theme={null} // QBSubscriptionListener // ... @Override public void subscriptionRequested(int userId) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // QBSubscriptionListener() // ... @Override public void subscriptionRequested(int userId) { } ``` </Tab> </Tabs> <Note> Maximum number of contacts is 300. </Note> ## Confirm the contact request To confirm the request, use `contactsRoster.confirmSubscription()` method. <Tabs> <Tab title="Java"> ```Java theme={null} try { contactsRoster.confirmSubscription(user.getId()); } catch (SmackException.NotConnectedException | SmackException.NotLoggedInException | SmackException.NoResponseException | XMPPException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { contactsRoster.confirmSubscription(user.id) } catch (exceptione: SmackException.NotConnectedException) { } catch (exceptione: SmackException.NotLoggedInException) { } catch (exceptione: SmackException.NoResponseException) { } catch (exception: XMPPException) { } ``` </Tab> </Tabs> ## Reject the contact request To reject the request, use the `contactsRoster.reject()` method. <Tabs> <Tab title="Java"> ```Java theme={null} try { contactsRoster.reject(user.getId()); } catch (SmackException.NotConnectedException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { contactsRoster.reject(user.id) } catch (exception: SmackException.NotConnectedException) { } ``` </Tab> </Tabs> ## Remove user from the contact list To remove a previously added user from the contact list, use the following method. <Tabs> <Tab title="Java"> ```Java theme={null} try { contactsRoster.unsubscribe(user.getId()); } catch (SmackException.NotConnectedException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { contactsRoster.unsubscribe(user.id) } catch (exception: SmackException.NotConnectedException) { } ``` </Tab> </Tabs> ## Contact list updates The above-specified `RosterListener` listener will give you all updates regarding contact list changes and users' status updates. # Dialogs Source: https://docs.quickblox.com/sdks/android-chat-dialogs Learn how to create and manage dialogs. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/android-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Dialog types All chats between users are organized in dialogs. There are 3 types of dialogs: * **private dialog** - a dialog between 2 users. * **group dialog** - a dialog between the specified list of users. * **public dialog** - an open dialog. Any user from your app can be joined to it. You need to create a new dialog and then use it to chat with other users. You also can obtain a list of your existing dialogs. ## Create dialog To create a **private** dialog, you need to set the dialog type to `QBDialogType.PRIVATE` and ID of an opponent you want to create a chat with. <Tabs> <Tab title="Java"> ```Java theme={null} ArrayList<Integer> occupantIdsList = new ArrayList<Integer>(); occupantIdsList.add(123); QBChatDialog dialog = new QBChatDialog(); dialog.setType(QBDialogType.PRIVATE); dialog.setOccupantsIds(occupantIdsList); // or just use DialogUtils //QBChatDialog dialog = DialogUtils.buildPrivateDialog(recipientId); QBRestChatService.createChatDialog(dialog).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog result, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val occupantIdsList = ArrayList<Int>() occupantIdsList.add(123) val dialog = QBChatDialog() dialog.type = QBDialogType.PRIVATE dialog.setOccupantsIds(occupantIdsList) // or just use DialogUtils //QBChatDialog dialog = DialogUtils.buildPrivateDialog(recipientId); QBRestChatService.createChatDialog(dialog).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(result: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> To create a **group** dialog for a predefined number of occupants, you need to set the dialog type to `QBDialogType.GROUP` and IDs of opponents you want to create a chat with using the `QBChatDialog` value. <Tabs> <Tab title="Java"> ```Java theme={null} ArrayList<Integer> occupantIdsList = new ArrayList<Integer>(); occupantIdsList.add(123); occupantIdsList.add(234); occupantIdsList.add(345); occupantIdsList.add(456); QBChatDialog dialog = new QBChatDialog(); dialog.setName("Group chat"); dialog.setType(QBDialogType.GROUP); dialog.setOccupantsIds(occupantIdsList); // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialog.setPhoto(""); // or just use DialogUtils // QBChatDialog dialog = DialogUtils.buildDialog("Chat with Friends", QBDialogType.GROUP, occupantIdsList); QBRestChatService.createChatDialog(dialog).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog result, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val occupantIdsList = ArrayList<Int>() occupantIdsList.add(123) occupantIdsList.add(234) occupantIdsList.add(345) occupantIdsList.add(456) val dialog = QBChatDialog() dialog.name = "Group chat" dialog.type = QBDialogType.GROUP dialog.setOccupantsIds(occupantIdsList) // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialog.photo = ""; // or just use DialogUtils // QBChatDialog dialog = DialogUtils.buildDialog("Chat with Friends", QBDialogType.GROUP, occupantIdsList); QBRestChatService.createChatDialog(dialog).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(result: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> It's possible to create a **public** dialog, so any user from your application can be joined to it. There is no list of occupants. This dialog is open for everybody. You just need to set `QBDialogType.PUBLIC_GROUP` as a dialog type. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatDialog dialog = new QBChatDialog(); dialog.setName("Public group chat"); dialog.setType(QBDialogType.PUBLIC_GROUP); // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialog.setPhoto(""); QBRestChatService.createChatDialog(dialog).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog result, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val dialog = QBChatDialog() dialog.name = "Public group chat" dialog.type = QBDialogType.PUBLIC_GROUP // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialog.photo = ""; QBRestChatService.createChatDialog(dialog).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(result: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Create dialog with custom parameters Any dialog can be extended with additional parameters whether it is a **private**, **group**, or **public**. These parameters can be used to store additional data. Also, these parameters can be used in dialogs retrieval requests. To start using additional parameters, create an additional schema of your parameters. This is a Custom Objects class. Just create an empty class with all fields that you need. These fields will be additional parameters in your dialog. See [this section](/sdks/android-custom-objects) to learn how to create a schema using Custom Objects. Then, specify the parameters defined in the schema in a new dialog. <Tabs> <Tab title="Java"> ```Java theme={null} private void createDialogWithCurtomParameters() { QBDialogCustomData customData = new QBDialogCustomData(); customData.putBoolean("customBoolean", true); customData.putInteger("customInteger", 327); customData.putString("customString", "Value"); customData.putDate("customDate", new Date()); customData.putFloat("customFloat", 327.2f); List<String> paramsList = new ArrayList<>(); paramsList.add("param1"); customData.putArray("nameOfCustomArray", paramsList); QBChatDialog chatDialog = new QBChatDialog(); chatDialog.setCustomData(customData); QBRestChatService.createChatDialog(chatDialog).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog chatDialog, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); } // now we can get the same parameters from any dialog we load private void getDataFromDialog(QBChatDialog chatDialog) { Integer customInteger = chatDialog.getCustomData().getInteger("customInteger"); String customString = chatDialog.getCustomData().getString("customString"); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} private fun createDialogWithCustomParameters() { val customData = QBDialogCustomData() customData.putBoolean("customBoolean", true) customData.putInteger("customInteger", 327) customData.putString("customString", "Value") customData.putDate("customDate", Date()) customData.putFloat("customFloat", 327.2f) val paramsList = ArrayList<String>() paramsList.add("param1") customData.putArray("nameOfCustomArray", paramsList) val chatDialog = QBChatDialog() chatDialog.customData = customData QBRestChatService.createChatDialog(chatDialog).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(chatDialog: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) } // now we can get the same parameters from any dialog we load private fun getDataFromDialog(chatDialog: QBChatDialog) { val chatDialog = qbChatDialog.customData.getInteger("customInteger") val chatDialog = qbChatDialog.customData.getString("customString") } ``` </Tab> </Tabs> ## Create group dialog with join required <Warning> Available since **QuickBlox Android SDK v4.4.0**. Prior to **server version 2.34.0**, all group dialogs required joining. Starting from **server version 2.34.0**, new applications do not require joining, while existing applications retain the previous behavior. You can change the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). If `isJoinRequired` is explicitly set when creating a dialog, the provided value takes priority over the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). Most applications do not need this feature. The default behavior where participants can send and receive real-time messages without joining is recommended for most use cases. </Warning> When creating a **group** dialog, you can set the `isJoinRequired` parameter to `true` to require participants to explicitly join the dialog before they can send or receive real-time messages. This is only needed when you want to restrict real-time messaging in specific dialogs until participants explicitly join. By default, `isJoinRequired` is `false` and participants can message without joining. This parameter applies **only to group dialogs**. You can change the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). <Tabs> <Tab title="Java"> ```Java theme={null} ArrayList<Integer> occupantIdsList = new ArrayList<Integer>(); occupantIdsList.add(123); occupantIdsList.add(234); QBChatDialog dialog = new QBChatDialog(); dialog.setName("Group chat"); dialog.setType(QBDialogType.GROUP); dialog.setOccupantsIds(occupantIdsList); dialog.setIsJoinRequired(true); QBRestChatService.createChatDialog(dialog).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog result, Bundle bundle) { boolean isJoinRequired = result.isJoinRequired(); } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val occupantIdsList = ArrayList<Int>() occupantIdsList.add(123) occupantIdsList.add(234) val dialog = QBChatDialog() dialog.name = "Group chat" dialog.type = QBDialogType.GROUP dialog.setOccupantsIds(occupantIdsList) dialog.setIsJoinRequired(true) QBRestChatService.createChatDialog(dialog).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(result: QBChatDialog?, bundle: Bundle?) { val isJoinRequired = result?.isJoinRequired } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Check if join required for group dialog <Info> The `isJoinRequired` field is available starting from **QuickBlox Android SDK v4.4.0**. See [Create group dialog with join required](/sdks/android-chat-dialogs#create-group-dialog-with-join-required) for details. </Info> You can get the `isJoinRequired` value for any group dialog: <Tabs> <Tab title="Java"> ```Java theme={null} boolean isJoinRequired = groupDialog.isJoinRequired(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val isJoinRequired = groupDialog.isJoinRequired ``` </Tab> </Tabs> ## Join group dialog <Info> Starting from **QuickBlox Android SDK v4.4.0**, joining a group dialog is required only when `isJoinRequired` is set to `true` for a dialog. See [Create group dialog with join required](/sdks/android-chat-dialogs#create-group-dialog-with-join-required) for details. </Info> If `isJoinRequired` is set to `true` for a group dialog, you need to join it by calling the `join()` method before you can send or receive real-time messages. See [this section](/sdks/android-chat-messaging#send-text-message) to learn how to send/receive real-time messages. You must join the dialog after every new connection or reconnection. If the connection is lost and then restored, whether manually or automatically, you need to call `join()` again for each dialog where `isJoinRequired` is `true`. <Tabs> <Tab title="Java"> ```Java theme={null} // Synchronous try { groupDialog.join(new DiscussionHistory()); } catch (XMPPException exception) { } catch (SmackException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // Synchronous try { groupDialog.join(DiscussionHistory()) } catch (exception: XMPPException) { } catch (exception: SmackException) { } ``` </Tab> </Tabs> <Tabs> <Tab title="Java"> ```Java theme={null} // Asynchronous groupDialog.join(new DiscussionHistory(), new QBEntityCallback<Void>() { @Override public void onSuccess(Void result, Bundle params) { } @Override public void onError(QBResponseException responseException) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // Asynchronous groupDialog.join(DiscussionHistory(), object : QBEntityCallback<Void> { override fun onSuccess(result: Void?, params: Bundle?) { } override fun onError(responseException: QBResponseException?) { } }) ``` </Tab> </Tabs> <Note> You can join a group dialog **only** if your user ID is present in the `occupantIDs` array in the dialog model. Your user ID is added to the `occupantIDs` array if you create a dialog **or** you are added to the dialog by another user. See [this section](/sdks/android-chat-dialogs#add-occupants) to learn how to add occupants to the group dialog. </Note> To check if you have already joined the dialog, call the appropriate method from the dialog model. <Tabs> <Tab title="Java"> ```Java theme={null} groupDialog.isJoined(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} groupDialog.isJoined ``` </Tab> </Tabs> ## Join public dialog Before you start chatting in a **public** dialog, you must join it by calling the `join()` method. Unlike group dialogs, joining a public dialog is always required. If you've successfully joined the dialog, you can send/receive real-time messages. See [this section](/sdks/android-chat-messaging#send-text-message) to learn how to send/receive real-time messages. You must join the dialog after every new connection or reconnection. If the connection is lost and then restored, whether manually or automatically, you need to call `join()` again. <Tabs> <Tab title="Java"> ```Java theme={null} // Synchronous try { publicDialog.join(new DiscussionHistory()); } catch (XMPPException exception) { } catch (SmackException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // Synchronous try { publicDialog.join(DiscussionHistory()) } catch (exception: XMPPException) { } catch (exception: SmackException) { } ``` </Tab> </Tabs> <Tabs> <Tab title="Java"> ```Java theme={null} // Asynchronous publicDialog.join(new DiscussionHistory(), new QBEntityCallback<Void>() { @Override public void onSuccess(Void result, Bundle params) { } @Override public void onError(QBResponseException responseException) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // Asynchronous publicDialog.join(DiscussionHistory(), object : QBEntityCallback<Void> { override fun onSuccess(result: Void?, params: Bundle?) { } override fun onError(responseException: QBResponseException?) { } }) ``` </Tab> </Tabs> To check if you have already joined the dialog, call the appropriate method from the dialog model. <Tabs> <Tab title="Java"> ```Java theme={null} publicDialog.isJoined(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} publicDialog.isJoined ``` </Tab> </Tabs> ## Leave group dialog You can leave the **group** dialog by calling the `leave()` method. After leaving, you will stop receiving real-time messages from this dialog. You need to join the dialog again to resume receiving real-time messages. <Info> Starting from **QuickBlox Android SDK v4.4.0**, leaving a group dialog is only needed when `isJoinRequired` is set to `true`. If `isJoinRequired` is `false`, you do not need to call `leave()`. </Info> <Tabs> <Tab title="Java"> ```Java theme={null} try { groupDialog.leave(); } catch (XMPPException | SmackException.NotConnectedException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { groupDialog.leave() } catch (exception: XMPPException) { } catch (exception: SmackException.NotConnectedException) { } ``` </Tab> </Tabs> <Note> When you leave a group dialog, your user ID is still present in the `occupantIDs` array in the dialog model. The dialog will still appear in the list of dialogs and you will still have access to the chat history. To remove yourself from the group dialog, use the `updateChatDialog()` method. See [this section](/sdks/android-chat-dialogs#remove-occupants) to learn how to remove occupants from the group dialog. </Note> ## Leave public dialog You can leave the **public** dialog by calling the `leave()` method. After leaving, you will stop receiving real-time messages from this dialog. You need to join the dialog again to resume receiving real-time messages. <Tabs> <Tab title="Java"> ```Java theme={null} try { publicDialog.leave(); } catch (XMPPException | SmackException.NotConnectedException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { publicDialog.leave() } catch (exception: XMPPException) { } catch (exception: SmackException.NotConnectedException) { } ``` </Tab> </Tabs> ## Retrieve online users You can get a list of dialog occupants who are currently online. Call the `requestOnlineUsers()` method to get the list of online users who are joined to the dialog. As a result, a list of user IDs is returned. <Tabs> <Tab title="Java"> ```Java theme={null} try { Collection<Integer> onlineUsersIds = chatDialog.requestOnlineUsers(); } catch (XMPPException.XMPPErrorException exception) { } catch (SmackException.NotConnectedException exception) { } catch (SmackException.NoResponseException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { val onlineUsersIds = chatDialog.requestOnlineUsers() } catch (exception: XMPPErrorException) { } catch (exception: NotConnectedException) { } catch (exception: NoResponseException) { } ``` </Tab> </Tabs> Let's see, how the `requestOnlineUsers()` method is used with regard to the dialog type | Capabilities | Public | Group | Private | | --------------------- | ------ | ----- | ------- | | Retrieve online users | ✗ | ✓ | ✗ | <Note> You can retrieve online users from the group dialog **only** if you are joined to it. </Note> ## Retrieve list of dialogs It's common to request all your dialogs on every app login. The request below will return **group** dialogs containing the `test` in the names, sorted in descending order, and limited to 50 dialogs on the page. <Tabs> <Tab title="Java"> ```Java theme={null} String field = "name"; String searchValue = "test"; QBDialogType type = QBDialogType.GROUP; QBRequestGetBuilder requestBuilder = new QBRequestGetBuilder(); requestBuilder.setLimit(50); requestBuilder.setSkip(0); requestBuilder.sortDesc(field); requestBuilder.in(field, searchValue); QBRestChatService.getChatDialogs(type, requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBChatDialog>>() { @Override public void onSuccess(ArrayList<QBChatDialog> result, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val field = "name" val searchValue = "test" val type = QBDialogType.GROUP val requestBuilder = QBRequestGetBuilder() requestBuilder.limit = 50 requestBuilder.skip = 0 requestBuilder.sortDesc(field) requestBuilder.in(field, searchValue) QBRestChatService.getChatDialogs(type, requestBuilder).performAsync(object : QBEntityCallback<java.util.ArrayList<QBChatDialog?>?> { override fun onSuccess(result: ArrayList<QBChatDialog?>?, bundle: Bundle) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------------- | -------- | ----------------------------------------------- | | type | yes | Dialog type. Pass null to retrieve all dialogs. | | requestBuilder | yes | Allows to set parameters for the request. | If you want to retrieve only dialogs updated after some specific date time and order the search results, you can apply operators. This is useful if you cache dialogs somehow and do not want to obtain the whole list of your dialogs on every app start. Thus, you can apply [search](/sdks/android-chat-dialogs#search-operators) and [sort](/sdks/android-chat-dialogs#sort-operators) operators to list dialogs on the page so that it is easier to view specific dialogs. The operators are set in the `QBRequestGetBuilder` class. If you want to get a paginated list of users from the server, you can set the following pagination parameters in the `QBRequestGetBuilder` class. | Pagination parameter | Description | | -------------------- | ------------------------------------------------------------------------------------------- | | skip | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | Limit search results to N records. Useful for pagination. Default value: **100**. | ### Search operators You can use search operators to get more specific search results. The request below will return **group** dialogs in the array containing `test` in their names. <Tabs> <Tab title="Java"> ```Java theme={null} String field = "name"; String searchValue = "test"; QBDialogType type = QBDialogType.GROUP; QBRequestGetBuilder requestBuilder = new QBRequestGetBuilder(); requestBuilder.in(field, searchValue); QBRestChatService.getChatDialogs(type, requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBChatDialog>>() { @Override public void onSuccess(ArrayList<QBChatDialog> result, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val field = "name" val searchValue = "test" val type = QBDialogType.GROUP val requestBuilder = QBRequestGetBuilder() requestBuilder.in(field, searchValue) QBRestChatService.getChatDialogs(type, requestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBChatDialog>> { override fun onSuccess(result: ArrayList<QBChatDialog>, bundle: Bundle) { } override fun onError(responseException: exception) { } }) ``` </Tab> </Tabs> Here are the methods that you can use to search for the exact data that you need. | Methods | Applicable to types | Applicable to fields | Description | | ----------------------- | -------------------- | --------------------------------------------------- | ---------------------------------------------------- | | lt(field, searchValue) | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** operator. | | lte(field, searchValue) | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** or **Equal** to operator. | | gt(field, searchValue) | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** operator. | | gte(field, searchValue) | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** or **Equal** to operator. | | ne(field, searchValue) | number, string, date | \_id, name, last\_message\_date\_sent | **Not Equal** to operator. | | in(field, searchValue) | number, string, date | type, last\_message\_date\_sent, name | **IN** array operator. | | nin(field, searchValue) | number, string, date | last\_message\_date\_sent | Not **IN** array operator. | | all(field, searchValue) | number | occupants\_ids | **ALL** are contained in array. | | ctn(field, searchValue) | string | name | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return **group** dialogs by the field sorted in descending order. <Tabs> <Tab title="Java"> ```Java theme={null} String field = "name"; QBDialogType type = QBDialogType.GROUP; QBRequestGetBuilder requestBuilder = new QBRequestGetBuilder(); requestBuilder.sortDesc(field); QBRestChatService.getChatDialogs(type, requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBChatDialog>>() { @Override public void onSuccess(ArrayList<QBChatDialog> result, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val field = "name" val type = QBDialogType.GROUP val requestBuilder = QBRequestGetBuilder() requestBuilder.sortDesc(field) QBRestChatService.getChatDialogs(type, requestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBChatDialog>> { override fun onSuccess(result: ArrayList<QBChatDialog>, bundle: Bundle) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> Here are the methods that you can use to order the search results. | Methods | Applicable to types | Applicable to fields | Description | | --------------- | ------------------- | ------------------------------------------------ | ------------------------------------------------------------------------- | | sortAsc(field) | All types | id, created\_at, name, last\_message\_date\_sent | Search results will be sorted in ascending order by the specified field. | | sortDesc(field) | All types | id, created\_at, name, last\_message\_date\_sent | Search results will be sorted in descending order by the specified field. | ## Update dialog You can update the information for a **private**, **group**, and **public** dialog. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatDialog dialog = new QBChatDialog(); dialog.setDialogId("dsfsd934329hjhkda98793j2"); // to make updates - the dialog must contain dialogId dialog.setName("Team room"); dialog.setPhoto("https://new_photo_url"); // or it can be ID of uploaded File with QBContent QBRequestUpdateBuilder requestBuilder = new QBRequestUpdateBuilder(); QBRestChatService.updateChatDialog(dialog, requestBuilder).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog updatedDialog, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val dialog = QBChatDialog() dialog.dialogId = "dsfsd934329hjhkda98793j2" // to make updates - the dialog must contain dialogId dialog.name = "Team room" dialog.photo = "https://new_photo_url" // or it can be ID of uploaded File with QBContent val requestBuilder = QBRequestUpdateBuilder() QBRestChatService.updateChatDialog(dialog, requestBuilder).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(updatedDialog: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | ------------------------ | -------------- | -------------- | -------------- | | Update a dialog name | Owner | Owner | ✗ | | Update a photo | Owner | Owner | ✗ | | Update custom parameters | Owner,Occupant | Owner,Occupant | Owner,Occupant | ## Add occupants You can add occupants in a **group** dialog by using the `addUsers()` method. As a result, your ID will be added to the `occupantIDs` array. <Tabs> <Tab title="Java"> ```Java theme={null} // to add users you should pass whole QBUser model or it's ID (user.getId)t QBDialogRequestBuilder requestBuilder = new QBDialogRequestBuilder(); QBChatDialog dialog = new QBChatDialog(); ArrayList<QBUser> usersToAdd = new ArrayList<QBUser>(); usersToAdd.add(QBUser()); usersToAdd.add(QBUser()); QBUser[] usersArray = new QBUser[usersToAdd.size()]; requestBuilder.addUsers(usersToAdd.toArray(usersArray)); QBRestChatService.updateChatDialog(dialog, requestBuilder).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog chatDialog, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // to add users you should pass whole QBUser model or it's ID (user.getId) val requestBuilder = QBDialogRequestBuilder() val dialog = QBChatDialog() val usersToAdd = ArrayList<QBUser>() usersToAdd.add(QBUser()) usersToAdd.add(QBUser()) requestBuilder.addUsers(*usersToAdd.toTypedArray()) QBRestChatService.updateChatDialog(dialog, requestBuilder).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(chatDialog: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------------- | -------- | --------------------------------------------------- | | dialog | yes | A dialog to add users to. | | requestBuilder | yes | Specifies requestBuilder fields that should be set. | Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | --------------- | ------------- | -------------- | -------------- | | Add other users | ✗ | Owner,Occupant | ✗ | ## Remove occupants You can remove occupants from a **group** dialog by using the `removeUsers()` method. As a result, the IDs will be removed the `occupantIDs` array. <Tabs> <Tab title="Java"> ```Java theme={null} // to remove users you should pass whole QBUser model or it's ID (user.getId) QBDialogRequestBuilder requestBuilder = new QBDialogRequestBuilder(); QBChatDialog dialog = new QBChatDialog(); ArrayList<QBUser> usersToRemove = new ArrayList<QBUser>(); usersToRemove.add(QBUser()); usersToRemove.add(QBUser()); QBUser[] usersArray = new QBUser[usersToRemove.size()]; requestBuilder.removeUsers(usersToRemove.toArray(usersArray)); QBRestChatService.updateChatDialog(dialog, requestBuilder).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog chatDialog, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // to remove users you should pass whole QBUser model or it's ID (user.getId) val requestBuilder = QBDialogRequestBuilder() val dialog = QBChatDialog() val usersToRemove = ArrayList<QBUser>() usersToRemove.add(QBUser()) usersToRemove.add(QBUser()) requestBuilder.removeUsers(*usersToRemove.toTypedArray()) QBRestChatService.updateChatDialog(dialog, requestBuilder).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(chatDialog: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------------- | -------- | --------------------------------------------------- | | dialog | yes | A dialog to remove users from. | | requestBuilder | yes | Specifies requestBuilder fields that should be set. | Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | ------------------ | ------------- | -------------- | -------------- | | Remove other users | ✗ | Owner | ✗ | | Remove yourself | ✗ | Owner,Occupant | ✗ | ## Delete dialog A request below will remove a dialog for a current user, but other users will be still able to chat there. <Tabs> <Tab title="Java"> ```Java theme={null} String dialogId = qbChatDialog.getDialogId(); boolean forceDelete = false; QBRestChatService.deleteDialog(dialogId, forceDelete).performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val dialogId = qbChatDialog.dialogId val forceDelete = false QBRestChatService.deleteDialog(dialogId, forceDelete).performAsync(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> Set the `forceDelete` parameter as `true` to completely remove the dialog for all users. You can also delete multiple dialogs in a single request. You can also use multiple dialogs deleting using the snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} StringifyArrayList<String> dialogsIds = new StringifyArrayList<>(); for (QBChatDialog dialog : dialogsIds) { dialogsIds.add(dialog.getDialogId()); } Bundle bundle = new Bundle(); boolean forceDelete = false; QBRestChatService.deleteDialogs(dialogsIds, forceDelete, bundle).performAsync(new QBEntityCallback<ArrayList<String>>() { @Override public void onSuccess(ArrayList<String> strings, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val dialogsIds = StringifyArrayList<String>() for (dialog in dialogsIds) { dialogsIds.add(dialog.dialogId) } val bundle = Bundle() val forceDelete = false QBRestChatService.deleteDialogs(dialogsIds, forceDelete, bundle).performAsync(object : QBEntityCallback<ArrayList<String>> { override fun onSuccess(strings: ArrayList<String>?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public | Group | Private | | ------------------------------------------------------------ | ------ | -------------- | -------------- | | Delete a dialog for all usersusing the forceDeleteparameter. | Owner | Owner | Owner | | Delete a dialog for a current user | Owner | Owner,Occupant | Owner,Occupant | ## Get number of dialogs You can get a number of dialogs using the `getChatDialogsCount()` method. The request below will return a count of all **group** dialogs. <Tabs> <Tab title="Java"> ```Java theme={null} QBRequestGetBuilder requestBuilder = new QBRequestGetBuilder(); String field = "type"; Bundle bundle = new Bundle(); requestBuilder.addRule(field, QueryRule.IN, QBDialogType.GROUP.getCode()); QBRestChatService.getChatDialogsCount(requestBuilder, bundle).performAsync(new QBEntityCallback<Integer>() { @Override public void onSuccess(Integer count, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val requestBuilder = QBRequestGetBuilder() val field = "type" val bundle = Bundle() requestBuilder.addRule(field, QueryRule.IN, QBDialogType.GROUP.code) QBRestChatService.getChatDialogsCount(requestBuilder, bundle).performAsync(object : QBEntityCallback<Int> { override fun onSuccess(count: Int, bundle: Bundle) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------------- | -------- | ------------------------------------------------ | | requestBuilder | yes | Allows to set parameters for the request. | | bundle | no | The bundle with response additional information. | ## Get number of unread messages To get a number of unread messages from a particular dialog, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBRestChatService.getChatDialogById(dialog.getDialogId()).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog chatDialog, Bundle bundle) { // get unread messages count int unreadMessageCount = chatDialog.getUnreadMessageCount(); } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBRestChatService.getChatDialogById(dialog.dialogId).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(chatDialog: QBChatDialog?, bundle: Bundle?) { let unreadMessageCount = chatDialog.unreadMessageCount } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> You can also retrieve **total** unread messages count by using the `getTotalUnreadMessagesCount()` method. <Tabs> <Tab title="Java"> ```Java theme={null} Set<String> dialogsIds = new HashSet<String>(); dialogsIds.add("44g9867978v894365tf7g3y4"); dialogsIds.add("47r8738478394ffdi39id493"); Bundle bundle = new Bundle(); QBRestChatService.getTotalUnreadMessagesCount(dialogsIds, bundle).performAsync(new QBEntityCallback<Integer>() { @Override public void onSuccess(Integer totalCount, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val dialogsIds = HashSet<String>() dialogsIds.add("44g9867978v894365tf7g3y4") dialogsIds.add("47r8738478394ffdi39id493") val bundle = Bundle() QBRestChatService.getTotalUnreadMessagesCount(dialogsIds, bundle).performAsync(object : QBEntityCallback<Int> { override fun onSuccess(integer: Int?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | dialogsIds | yes | IDs of dialogs.- If dialogsIds are **not** specified, the total number of unread messages for **all** dialogs of the user will be returned.- If dialogsIds are specified, the number of unread messages for each specified dialog will be returned. Also, the total number of unread messages for all dialogs of the user will be returned. | ## Resources A sequence of steps a user takes to start a dialog by moving through the application lifecycle. <img /> # Messaging Source: https://docs.quickblox.com/sdks/android-chat-messaging Learn how to send and receive messages, mark messages as delivered or read, etc. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/android-chat-connection) page to learn how to do it. 5. Create a dialog. See [Dialogs](/sdks/android-chat-dialogs) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Subscribe message events To add the listener to receive messages in real-time, use the `addDialogMessageListener()` method. The listener enables the app to listen to the incoming message. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatService chatService = QBChatService.getInstance(); QBIncomingMessagesManager incomingMessagesManager = chatService.getIncomingMessagesManager(); incomingMessagesManager.addDialogMessageListener(new QBChatDialogMessageListener() { @Override public void processMessage(String dialogId, QBChatMessage chatMessage, Integer senderId) { } @Override public void processError(String dialogId, QBChatException exception, QBChatMessage chatMessage, Integer senderId) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val chatService = QBChatService.getInstance() val incomingMessagesManager = chatService.incomingMessagesManager incomingMessagesManager.addDialogMessageListener(object : QBChatDialogMessageListener { override fun processMessage(dialogId: String?, chatMessage: QBChatMessage?, senderId: Int?) { } override fun processError(dialogId: String?, exception: QBChatException?, chatMessage: QBChatMessage?, senderId: Int?) { } }) ``` </Tab> </Tabs> You can add a message listener for a particular dialog if needed. <Tabs> <Tab title="Java"> ```Java theme={null} privateDialog.addMessageListener(new QBChatDialogMessageListener() { @Override public void processMessage(String dialogId, QBChatMessage chatMessage, Integer senderId) { } @Override public void processError(String dialogId, QBChatException exception, QBChatMessage chatMessage, Integer senderId) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} privateDialog.addMessageListener(object : QBChatDialogMessageListener { override fun processMessage(dialogId: String?, qbChatMessage: QBChatMessage?, senderId: Int?) { } override fun processError(dialogId: String?, e: QBChatException?, qbChatMessage: QBChatMessage?, senderId: Int?) { } }) ``` </Tab> </Tabs> ## Send text message To send a message to a **private**, **group** or **public** dialog, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setBody("Hello QuickBlox!"); chatMessage.setSaveToHistory(true); // if you want to use this feature without callbacks: //try{ // privateDialog.sendMessage(chatMessage); //} catch (SmackException.NotConnectedException e) { // //} dialog.sendMessage(chatMessage, new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val chatMessage = QBChatMessage() chatMessage.body = "Hello QuickBlox!" chatMessage.setSaveToHistory(true) // If you want to use this feature without callbacks: //try{ // privateDialog.sendMessage(chatMessage); //} catch (SmackException.NotConnectedException e) { // //} dialog.sendMessage(chatMessage, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> <Note> You need to join the **group** and **public** dialog by calling the `join()` method before you start chatting in a dialog. Once the dialog is joined, you can receive/send messages. See [this section](/sdks/android-chat-dialogs#join-dialog) to learn how to join the dialog. </Note> There are two ways to send any message to any dialog: 1. Synchronous. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setBody("Hello QuickBlox!"); chatMessage.setSaveToHistory(true); try { dialog.sendMessage(chatMessage); } catch (SmackException.NotConnectedException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val chatMessage = QBChatMessage() chatMessage.body = "Hello QuickBlox!" chatMessage.setSaveToHistory(true) try { dialog.sendMessage(chatMessage) } catch (exception: SmackException.NotConnectedException) { } ``` </Tab> </Tabs> 2. Asynchronous. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setBody("Hello QuickBlox!"); chatMessage.setSaveToHistory(true); dialog.sendMessage(chatMessage, new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val chatMessage = QBChatMessage() chatMessage.body = "Hello QuickBlox!" chatMessage.setSaveToHistory(true) dialog.sendMessage(chatMessage, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(e: QBResponseException?) { } }) ``` </Tab> </Tabs> You can use the most convenient for you, according to your application logic. <Tip> Make sure to set the `saveToHistory` as `true` to save the message on the server. If the `saveToHistory` is set as `false`, the message won't be saved on the server. However, the message will be delivered to the user in either case. </Tip> ## Send message with attachment Chat attachments are supported by the [content API](/sdks/android-content). In order to send a chat attachment, you need to upload the file to QuickBlox cloud storage and obtain a link to the file (file UID). Then you need to include this UID into the chat message and send it. <Tabs> <Tab title="Java"> ```Java theme={null} File filePhoto = new File("image.png"); boolean fileIsPublic = false; String [] tags = new String[]{"tag_1", "tag_2"}; QBContent.uploadFileTask(filePhoto, fileIsPublic, String.valueOf(tags), new QBProgressCallback() { @Override public void onProgressUpdate(int i) { // i - progress in percents } }).performAsync(new QBEntityCallback<QBFile>() { @Override public void onSuccess(QBFile file, Bundle bundle) { // create a message QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setSaveToHistory(true); // save a message to history // attach a photo QBAttachment attachment = new QBAttachment("photo"); attachment.setId(file.getId().toString()); chatMessage.addAttachment(attachment); // send the message // ... } @Override public void onError(QBResponseException exception) { // error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val filePhoto = File("image.png") val fileIsPublic = false val tags = arrayOf("tag_1", "tag_2") QBContent.uploadFileTask(filePhoto, fileIsPublic, tags.toString()) { // i - progress in percents }.performAsync(object : QBEntityCallback<QBFile> { override fun onSuccess(file: QBFile?, bundle: Bundle?) { // create a message val chatMessage = QBChatMessage() chatMessage.setSaveToHistory(true) // Save a message to history // attach a photo val attachment = QBAttachment("photo") attachment.id = file.id.toString() chatMessage.addAttachment(attachment) // send the message // ... } override fun onError(exception: QBResponseException?) { // error } }) ``` </Tab> </Tabs> The same flow is supported on the message receiver's side. When you receive a message with an attachment, you need to get the file UID, and then download the file from the cloud storage. <Tabs> <Tab title="Java"> ```Java theme={null} // QBChatDialogMessageListener @Override public void processMessage(String dialogId, QBChatMessage chatMessage, Integer integer) { // count of attachments might be more than one for (QBAttachment attachment : chatMessage.getAttachments()) { String fileId = attachment.getId(); // download a file QBContent.downloadFile(fileId).performAsync(new QBEntityCallback<InputStream>() { @Override public void onSuccess(InputStream inputStream, Bundle bundle) { // process file } @Override public void onError(QBResponseException exception) { } }); } } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // QBChatDialogMessageListener override fun processMessage(dialogId: String, chatMessage: QBChatMessage, integer: Int?) { // count of attachments might be more than one for (attachment in qbChatMessage.attachments) { val fileId = attachment.id // download a file QBContent.downloadFile(fileId).performAsync(object : QBEntityCallback<InputStream> { override fun onSuccess(inputStream: InputStream?, bundle: Bundle?) { // process file } override fun onError(exception: QBResponseException?) { } }) } } ``` </Tab> </Tabs> ## Send message with extra data You have an option to extend the message with additional fields. Specify one or more **key-value** items in the `message`. Using these items, you can implement the ability for a user to send self-location information to another user or notification messages signifying that a user has left a group, etc. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setSaveToHistory(true); chatMessage.setBody("How are you today?"); chatMessage.setProperty("customParam1", "book"); chatMessage.setProperty("customParam2", "21"); qbChatDialog.sendMessage(chatMessage, new QBEntityCallback<Void>() { @Override public void onSuccess(Void void, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val chatMessage = QBChatMessage() chatMessage.setSaveToHistory(true) chatMessage.isMarkable = true chatMessage.body = "How are you today?" chatMessage.setProperty("customParam1", "book") chatMessage.setProperty("customParam2", "21") qbChatDialog.sendMessage(chatMessage, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | ------------- | -------- | -------------------------------------------- | | qbChatMessage | yes | Specifies message fields that should be set. | Set the following fields of the `qbChatMessage`: | Field | Required | Description | | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- | | saveToHistory | no | Specifies if the message will be saved on the server. Set the saveToHistory as true to save the message on the server. | | body | no | A message text. | | property | no | Extra data. Specify any key-value pairs. In each pair, the key and value are both string values. | ## Retrieve chat history Every dialog stores its chat history that you can retrieve using the `getDialogMessages()` method. The request below will return messages for a specific dialog limited to the 100 dialogs on the page. <Tabs> <Tab title="Java"> ```Java theme={null} QBMessageGetBuilder messageGetBuilder = new QBMessageGetBuilder(); messageGetBuilder.setLimit(100); // if you want to retrieve messages using filtering: //messageGetBuilder.gte("date_sent", "508087800"); //messageGetBuilder.lte("date_sent", "1170720000"); //messageGetBuilder.markAsRead(false); QBRestChatService.getDialogMessages(chatDialog, messageGetBuilder).performAsync(new QBEntityCallback<ArrayList<QBChatMessage>>() { @Override public void onSuccess(ArrayList<QBChatMessage> chatMessages, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val messageGetBuilder = QBMessageGetBuilder() messageGetBuilder.limit = 100 // If you want to retrieve messages using filtering: //messageGetBuilder.gte("date_sent", "508087800") //messageGetBuilder.lte("date_sent", "1170720000") //messageGetBuilder.markAsRead(false) QBRestChatService.getDialogMessages(chatDialog, messageGetBuilder).performAsync(object : QBEntityCallback<ArrayList<QBChatMessage>> { override fun onSuccess(chatMessages: ArrayList<QBChatMessage>?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> <Note> If you want to mark all retrieved chat messages as a read, set the `markAsRead` parameter as `true`. If you decide not to mark chat messages as read, just set `markAsRead` parameter as `false`. </Note> If you want to retrieve only messages updated after some specific date time and order the search results, you can apply operators. This is useful if you cache messages somehow and do not want to obtain the whole list of messages on every app start. Thus, you can apply [search](/sdks/android-chat-messaging#search-operators) and [sort](/sdks/android-chat-messaging#sort-operators) operators to list messages on the page so that it is easier to view specific messages. The operators are set in the `QBMessageGetBuilder` class. If you want to get a paginated list of messages from the server, you can set the following pagination parameters in the `QBMessageGetBuilder` class. | Pagination parameter | Description | | -------------------- | ------------------------------------------------------------------------------------------- | | skip | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | Limit search results to N records. Useful for pagination. Default value: **100**. | ### Search operators You can use search operators to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ----------------------- | -------------------- | ---------------------------------------------------- | ---------------------------------------------------- | | lt(field, searchValue) | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** operator. | | lte(field, searchValue) | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** or **Equal** to operator. | | gt(field, searchValue) | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** operator. | | gte(field, searchValue) | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** or **Equal** to operator. | | ne(field, searchValue) | number, string, date | \_id, message, date\_sent, sender\_id, recipient\_id | **Not Equal** to operator. | | in(field, searchValue) | number, string, date | date\_sent, sender\_id, recipient\_id | **IN** array operator. | | nin(field, searchValue) | number, string, date | date\_sent, sender\_id, recipient\_id | Not **IN** array operator. | | or(field, searchValue) | number, string, date | date\_sent, sender\_id, recipient\_id | All records that contain a value 1 **or** value 2. | | ctn(field, searchValue) | string | message | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. | Sort operator | Applicable to types | Description | | --------------- | ------------------- | ------------------------------------------------------------------------- | | sortAsc(field) | All types | Search results will be sorted in ascending order by the specified field. | | sortDesc(field) | All types | Search results will be sorted in descending order by the specified field. | ## Update message To update a message text , use the `updateMessage()` method below. <Tabs> <Tab title="Java"> ```Java theme={null} QBMessageUpdateBuilder messageUpdateBuilder = new QBMessageUpdateBuilder(); messageUpdateBuilder.updateText("Updated message body string"); // if you want to mark message as Delivered or Read on the server //messageUpdateBuilder.markDelivered(); //messageUpdateBuilder.markRead(); QBRestChatService.updateMessage(message.getId(), message.getDialogId(), messageUpdateBuilder).performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val messageUpdateBuilder = QBMessageUpdateBuilder() messageUpdateBuilder.updateText("Updated message body string") // If you want to mark message as Delivered or Read on the server //messageUpdateBuilder.markDelivered(); //messageUpdateBuilder.markRead(); QBRestChatService.updateMessage(message.id, message.dialogId, messageUpdateBuilder).performAsync(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> <Tip> You can also use the `updateMessage()` method to mark message as delivered or read on the server. As a result, the ID of the user who has received the message or read it will be added to the array of `delivered_ids`/`read_ids` field, in the message model. However, the server won't notify the sender with delivery or read receipt that informs about the change in the message model. </Tip> ## Delete message Any user in the `occupantIDs` can delete a message from the dialog. As a result, the message will be deleted from the current user history, without affecting the histories of other users. The owner of the dialog can completely remove messages from all users' histories. This is achieved by setting the `forceDelete` parameter to `true`. <Tabs> <Tab title="Java"> ```Java theme={null} Set<String> messagesIds = new HashSet<>(); messagesIds.add("456abcdefg9876lmnop23qrst"); messagesIds.add("456gfedcba5432ponml09xyz0"); boolean forceDelete = false; QBRestChatService.deleteMessages(messagesIds, forceDelete).performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val messagesIds = HashSet<String>() messagesIds.add("456abcdefg9876lmnop23qrst") messagesIds.add("456gfedcba5432ponml09xyz0") val forceDelete = false QBRestChatService.deleteMessages(messagesIds, forceDelete).performAsync(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | ----------- | -------- | ------------------------------------------------------------------------------------------------------ | | messagesIDs | yes | A set of strings with messages IDs. | | forceDelete | yes | A boolean parameter. Delete message for everyone. Set it as true to perform. Only the owner can do it. | ## Check if a message is sent The message is considered as **sent** if it has been delivered to the server. To get to know that a message has been delivered to the server, make sure to enable a stream management before connecting to the Chat server. See [this section](/sdks/android-setup#stream-management) to learn how to enable the stream management. Thus, to track the event when your message becomes sent, use the `QBChatDialogMessageSentListener`. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatDialogMessageSentListener messageSentListener = new QBChatDialogMessageSentListener() { @Override public void processMessageSent(String dialogId, QBChatMessage chatMessage) { } @Override public void processMessageFailed(String dialogId, QBChatMessage chatMessage) { } }; chatDialog.addMessageSentListener(messageSentListener); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val messageSentListener = object : QBChatDialogMessageSentListener { override fun processMessageSent(dialogId: String?, chatMessage: QBChatMessage?) { } override fun processMessageFailed(dialogId: String?, chatMessage: QBChatMessage?) { } } chatDialog.addMessageSentListener(messageSentListener) ``` </Tab> </Tabs> <Warning> You should enable Stream Management before you do the `chatService.login()` because the Stream Management is initialized while Chat login is performed. The Stream Management defines an extension for active management of a stream between a client and server, including features for stanza acknowledgments. </Warning> ## Mark message as delivered As a sender, you may want to be informed that a message has been successfully delivered to the recipient. The mark-as-delivered functionality allows to notify the sender about message delivery. To track the event when the message has been delivered to the user, use the `QBMessageStatusListener()`. <Tabs> <Tab title="Java"> ```Java theme={null} QBMessageStatusesManager messageStatusesManager = QBChatService.getInstance().getMessageStatusesManager(); messageStatusesManager.addMessageStatusListener(new QBMessageStatusListener() { @Override public void processMessageDelivered(String messageId, String dialogId, Integer userId) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val messageStatusesManager = QBChatService.getInstance().messageStatusesManager messageStatusesManager.addMessageStatusListener(object : QBMessageStatusListener { override fun processMessageDelivered(messageId: String?, dialogId: String?, userId: Int?) { } }) ``` </Tab> </Tabs> Use the `deliverMessage()` method to mark a message as delivered. As a result, the server will notify a sender about the delivery receipt. <Tabs> <Tab title="Java"> ```Java theme={null} // to manually notify the server that the message has been delivered to you try { dialog.deliverMessage(message); } catch (XMPPException | SmackException.NotConnectedException e) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // to manually notify the server that the message has delivered to you try { dialog.deliverMessage(message) } catch (e: XMPPException) { } catch (e: SmackException.NotConnectedException) { } ``` </Tab> </Tabs> A message can be marked as delivered automatically by the server once a message is successfully delivered to the recipient. Set the `markable` as `true` using the `sendMessage()` method if you want, as a sender, to receive message delivery receipts from other recipients. Thus, the `markable` parameter enables the sender to request the delivery receipt. It also enables the recipient to confirm the message delivery. However, if `markable` is `false` or omitted, then you can notify a sender about the delivery receipt using the `deliverMessage()` method. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setBody("This is your message body"); chatMessage.setMarkable(true); // to be markable as delivered chatMessage.setSaveToHistory(true); // to be saved in chat history qbChatDialog.sendMessage(chatMessage, new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { // message successfully sent } @Override public void onError(QBResponseException exception) { // message Sending Error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val qbChatMessage = QBChatMessage() qbChatMessage.body = "This is your message body" qbChatMessage.isMarkable = true //To be markable as delivered qbChatMessage.setSaveToHistory(true) // To be saved in chat history qbChatDialog.sendMessage(qbChatMessage, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, p1: Bundle?) { // message successfully sent } override fun onError(exception: QBResponseException?) { // message Sending Error } }) ``` </Tab> </Tabs> <Warning> Make sure to understand, that marking-as-delivered operation just confirms the fact of message delivery. The message acquires the **delivered** status when the message delivered event is received. When a message is marked as delivered, the IDs of users who have received the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who received the message using the `getDialogMessages()` method. See [this section](/sdks/android-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Warning> ## Mark message as read As a sender, you may want to be informed that a message has been read by the recipient. The mark-as-read functionality allows to notify the sender that a message has been read. To track the event when the message has been read by the user, use the `QBMessageStatusListener()`. <Tabs> <Tab title="Java"> ```Java theme={null} QBMessageStatusesManager messageStatusesManager = QBChatService.getInstance().getMessageStatusesManager(); messageStatusesManager.addMessageStatusListener(new QBMessageStatusListener() { @Override public void processMessageRead(String messageId, String dialogId, Integer userId) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val messageStatusesManager = QBChatService.getInstance().messageStatusesManager messageStatusesManager.addMessageStatusListener(object : QBMessageStatusListener { override fun processMessageRead(messageId: String?, dialogId: String?, userId: Int?) { } }) ``` </Tab> </Tabs> Use the `readMessage()` method to mark a message as read. As a result, the server will notify a sender about the read receipt. <Tabs> <Tab title="Java"> ```Java theme={null} // to notify the server about displaying message to the user try { dialog.readMessage(message); } catch (XMPPException | SmackException.NotConnectedException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // to notify the server about displaying message to the user try { dialog.readMessage(message) } catch (exception: XMPPException) { } catch (exception: SmackException.NotConnectedException) { } ``` </Tab> </Tabs> <Warning> When a message is marked as read, the IDs of users who have read the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who read the message using the `getDialogMessages()` method. See [this section](/sdks/android-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Warning> ## Send typing indicators You may want, as a sender, to let the recipient know that you are typing the message or have stopped typing the message. Use typing indicators as a form of chat-specific presence. Typing indicators allow to indicate if users are typing messages in a dialog at the moment. There are the following **typing** notifications supported. * **typing**. The user is composing a message. The user is actively interacting with a message input interface specific to this chat session (for example, by typing in the input area of a chat window). * **stopped**. The user had been composing but now has stopped. The user has been composing but has not interacted with the message input interface for a short period of time (for example, 30 seconds). To track the event when the sender is typing the message, use the `QBChatDialogTypingListener()`. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatDialogTypingListener typingListener = new QBChatDialogTypingListener() { @Override public void processUserIsTyping(String dialogId, Integer userId) { } }; dialog.addIsTypingListener(typingListener); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val typingListener = object : QBChatDialogTypingListener { override fun processUserIsTyping(dialogId: String?, userId: Int?) { } } dialog.addIsTypingListener(typingListener) ``` </Tab> </Tabs> To track the event when the sender has stopped typing the message, use the `QBChatDialogTypingListener()`. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatDialogTypingListener typingListener = new QBChatDialogTypingListener() { @Override public void processUserStopTyping(String dialogId, Integer userId) { } }; dialog.addIsTypingListener(typingListener); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val typingListener = object : QBChatDialogTypingListener { override fun processUserStopTyping(dialogId: String?, userId: Int?) { } } dialog.addIsTypingListener(typingListener) ``` </Tab> </Tabs> To notify a recipient that a sender is typing the message, use the `sendIsTyping()` method. <Tabs> <Tab title="Java"> ```Java theme={null} // when user starts typing in the message input field try { dialog.sendIsTypingNotification(); } catch (XMPPException | SmackException.NotConnectedException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // When user starts typing in the message input field try { dialog.sendIsTypingNotification() } catch (exception: XMPPException) { } catch (exception: SmackException.NotConnectedException) { ``` </Tab> </Tabs> To notify a recipient that a sender had been composing a message but now has stopped, use the `sendStopTypingNotification()` method. <Tabs> <Tab title="Java"> ```Java theme={null} // when the user stops typing and not interact with message input field try { dialog.sendStopTypingNotification(); } catch (XMPPException | SmackException.NotConnectedException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // When the user stops typing and not interact with message input field try { dialog.sendStopTypingNotification() } catch (exception: XMPPException) { } catch (exception: SmackException.NotConnectedException) { ``` </Tab> </Tabs> ## Send system messages There is a way to send system messages to other users about some events. For example, a system message can be sent when a user has joined or left a group dialog. These messages are handled over a separate channel and are not be mixed up with regular chat messages. Thus, they are handled by the `QBSystemMessageListener` listener. See how to add the listener in the snippet below. System messages are also not shown in the dialog history and, consequently, are not stored on the server. This means that these messages will be delivered **only** to online users. Send system messages using the `sendSystemMessage()` method. <Tabs> <Tab title="Java"> ```Java theme={null} private QBSystemMessagesManager systemMessagesManager; private QBSystemMessageListener systemMessageListener; void setSystemMessagesManager() { systemMessagesListener = new QBSystemMessageListener(){ @Override public void processMessage(QBChatMessage chatMessage) { // new System Message Received } @Override public void processError(QBChatException exception, QBChatMessage chatMessage) { // new System Message Received with Exception } }; systemMessagesManager = QBChatService.getInstance().getSystemMessagesManager(); systemMessagesManager.addSystemMessageListener(systemMessagesListener); } void sendSystemMessage(QBChatDialog chatDialog, Integer opponentId) { QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setDialogId(chatDialog.getDialogId()); chatMessage.setRecipientId(opponentId); chatMessage.setProperty("custom_property_1", "custom_value_1"); chatMessage.setProperty("custom_property_2", "custom_value_2"); chatMessage.setProperty("custom_property_3", "custom_value_3"); try { systemMessagesManager.sendSystemMessage(chatMessage); } catch (SmackException.NotConnectedException exception) { exception.printStackTrace(); } } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} private lateinit var systemMessagesListener: QBSystemMessageListener private lateinit var systemMessagesManager: QBSystemMessagesManager fun setSystemMessagesManager() { systemMessagesListener = object : QBSystemMessageListener { override fun processMessage(chatMessage: QBChatMessage) { // new System Message Received } override fun processError(exception: QBChatException, chatMessage: QBChatMessage) { // new System Message Received with Exception } } systemMessagesManager = QBChatService.getInstance().systemMessagesManager systemMessagesManager.addSystemMessageListener(systemMessagesListener) } fun sendSystemMessage(qbChatDialog: chatDialog, opponentId: Int?) { val chatMessage = QBChatMessage() chatMessage.dialogId = chatDialog.dialogId chatMessage.recipientId = opponentId chatMessage.setProperty("custom_property_1", "custom_value_1") chatMessage.setProperty("custom_property_2", "custom_value_2") chatMessage.setProperty("custom_property_3", "custom_value_3") try { systemMessagesManager.sendSystemMessage(chatMessage) } catch (exception: SmackException.NotConnectedException) { exception.printStackTrace() } } ``` </Tab> </Tabs> | Argument | Description | | ------------- | --------------------------------------------------- | | qbChatMessage | Specifies system message fields that should be set. | Set the following fields of the `qbChatMessage`: | Field | Required | Description | | ----------- | -------- | ------------------------------------------------------------------------------------------------ | | recipientId | yes | ID of the recipient. | | dialogId | no | ID of the dialog. | | property | no | Extra data. Specify any key-value pairs. In each pair, the key and value are both string values. | # Offline Messaging Source: https://docs.quickblox.com/sdks/android-chat-offline-messaging Learn how to receive push notifications when the app is not running. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/android-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Enable offline messaging Send notifications about new messages to the user when their app is not running by using offline messaging functionality. This functionality allows to customize automatic push notifications displayed on a user's device when they are offline. In other words, if your opponent is offline when you are writing a message, they automatically receive a push notification. <Note> Make sure to subscribe your users to pushes using SDKs. Review [Push Notifications](/sdks/ios-push-notifications) section for more details. </Note> <Warning> Only FCM data messages can be sent from the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages in your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notifications. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> Enable offline messaging from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Check the **Enable automatic push notifications for offline users** if you want to receive push notifications from the server automatically. <Frame> <img alt="Enable Offline Messaging" /> </Frame> ## Configure offline messaging template You can configure an offline messaging template from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Choose the template in the **Templates** section. <Note> Available for Growth/HIPAA Cloud/Enterprise plan You can set the custom template in the **Offline messaging text** section using the following template variables: * %unread\_count% - total number of new messages the recipient has * %plural\[new message]% - words in brackets (20 characters max) will be pluralized based on unread messages count * %body\[x]% - quote x characters from message body * %recipient\_fullname% - full name of recipient * %sender\_fullname% - full name of sender * %recipient\_login% - login of recipient * %sender\_login% - login of sender * %dialog\_name% - name of chat dialog (will be replaced with "Private dialog" string for 1-1 messages) </Note> 3. Set **Badge counter** to include counter info into your push message. Useful to include an unread counter that is the number of unread messages a user has. 4. Set push notification sound in the **Sound** section (for iOS only). 5. Set the **Category (iOS)** field. Enter "actions" that should be presented in the app notification in various views (for iOS only). 6. Enable the **Content Available** option to indicate that new content is available (for iOS only). 7. Enable the **Mutable content** option to modify the content of newly delivered notifications before the user is alerted. Use a notification service app extension to be able to modify the content of the notification. If the option is enabled, the system passes the notification to the service app extension for the subsequent modification of the notification payload before the actual delivery (for iOS only). 8. Click the **Save** button. <Frame> <img alt="Configure Template" /> </Frame> <Warning> Currently, push notifications are supported in a mobile environment only. </Warning> # Privacy List Source: https://docs.quickblox.com/sdks/android-chat-privacy-list Learn how to create privacy lists and implement user-to-user blocks. Privacy list API allows enabling or disabling communication with other users in a chat. You can create, modify, delete privacy lists or define a default list. <Note> The user can have multiple privacy lists, but only one can be active. </Note> Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/android-chat-connection) page to learn how to do it. ## Create privacy list A privacy list must have at least one element in order to be created. If no elements are specified, then the list with a given name will be deleted. <Tabs> <Tab title="Java"> ```Java theme={null} QBPrivacyListsManager privacyListsManager = QBChatService.getInstance().getPrivacyListsManager(); QBPrivacyList privacyList = new QBPrivacyList(); privacyList.setName("My_Privacy_List"); ArrayList<QBPrivacyListItem> items = new ArrayList<>(); QBPrivacyListItem item = new QBPrivacyListItem(); item.setAllow(false); item.setType(QBPrivacyListItem.Type.USER_ID); item.setValueForType(String.valueOf(user.getId())); items.add(item); privacyList.setItems(items); try { privacyListsManager.createPrivacyList(privacyList); } catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val privacyListsManager = QBChatService.getInstance().privacyListsManager val privacyList = QBPrivacyList() privacyList.name = "My_Privacy_List" val items = ArrayList<QBPrivacyListItem>() val item = QBPrivacyListItem() item.isAllow = false item.type = QBPrivacyListItem.Type.USER_ID item.valueForType = user.id.toString() items.add(item) privacyList.items = items try { privacyListsManager.createPrivacyList(privacyList) } catch (exception: SmackException.NotConnectedException) { } catch (exception: SmackException.NoResponseException) { } catch (exception: XMPPException) { } ``` </Tab> </Tabs> The `QBPrivacyListItem` class takes 4 arguments: * **type** - use `USER_ID` to block a user in a 1-1 chat or `GROUP_USER_ID` to block in a group chat. * **valueForType** - ID of a user to apply an action. * **allow** - can be `true` or `false`. * **mutualBlock** - can be `true` or `false` to block user's message in both directions or not. <Note> In order to be used the privacy list should be not only set but also activated (set as default). </Note> ## Activate privacy list In order to activate rules from a privacy list, you should set it as default. <Tabs> <Tab title="Java"> ```Java theme={null} try { privacyListsManager.applyPrivacyList("My_Privacy_List"); } catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { privacyListsManager.applyPrivacyList("My_Privacy_List") } catch (exceptione: SmackException.NotConnectedException) { } catch (exception: SmackException.NoResponseException) { } catch (exception: XMPPException) { } ``` </Tab> </Tabs> ## Update privacy list There are some rules you should follow to update a privacy list: * Include **all** of the desired items (not a "delta"). * If you want to update or set a new privacy list instead of the current one, you should decline the current default list first. <Tabs> <Tab title="Java"> ```Java theme={null} // deactivate active list try { privacyListsManager.declinePrivacyList(); } catch (SmackException | XMPPException.XMPPErrorException exception) { } // create new list // ... // activate again active list try { privacyListsManager.applyPrivacyList("New_Privacy_List"); } catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // deactivate active list try { privacyListsManager.declinePrivacyList() } catch (exception: SmackException) { } catch (exception: XMPPException.XMPPErrorException) { } // create new list // ... // activate again active list try { privacyListsManager.applyPrivacyList("New_Privacy_List") } catch (exception: SmackException.NotConnectedException) { } catch (exception: SmackException.NoResponseException) { } catch (exception: XMPPException) { } ``` </Tab> </Tabs> ## Retrieve privacy lists To get a list of **all** your privacy lists names, use the following request. <Tabs> <Tab title="Java"> ```Java theme={null} QBPrivacyListsManager privacyListsManager = QBChatService.getInstance().getPrivacyListsManager(); // get all privacy lists List<QBPrivacyList> lists = new ArrayList<>(); try { lists = privacyListsManager.getPrivacyLists(); } catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val privacyListsManager = QBChatService.getInstance().privacyListsManager // get all privacy lists var lists: List<QBPrivacyList> = ArrayList() try { lists = privacyListsManager.privacyLists } catch (exception: SmackException.NotConnectedException) { } catch (exception: SmackException.NoResponseException) { } catch (exception: XMPPException) { } ``` </Tab> </Tabs> ## Retrieve privacy list by name To get the privacy list by name, you should use the following method. <Tabs> <Tab title="Java"> ```Java theme={null} QBPrivacyListsManager privacyListsManager = QBChatService.getInstance().getPrivacyListsManager(); // get privacy list by name QBPrivacyList privacyList = new QBPrivacyList(); try { privacyList = privacyListsManager.getPrivacyList("New_Privacy_List"); } catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val privacyListsManager = QBChatService.getInstance().privacyListsManager // get privacy list by name var privacyList = QBPrivacyList() try { privacyList = privacyListsManager.getPrivacyList("New_Privacy_List") } catch (exception: SmackException.NotConnectedException) { } catch (exception: SmackException.NoResponseException) { } catch (exception: XMPPException) { } ``` </Tab> </Tabs> ## Remove privacy list To delete a list, you can call a method below or you can edit a list and set items to `nil`. <Tabs> <Tab title="Java"> ```Java theme={null} QBPrivacyListsManager privacyListsManager = QBChatService.getInstance().getPrivacyListsManager(); try { privacyListsManager.declinePrivacyList(); privacyListsManager.deletePrivacyList("New_Privacy_List"); } catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException exception) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val privacyListsManager = QBChatService.getInstance().privacyListsManager try { privacyListsManager.declinePrivacyList() privacyListsManager.deletePrivacyList("New_Privacy_List") } catch (exception: SmackException.NotConnectedException) { } catch (exception: SmackException.NoResponseException) { } catch (exception: XMPPException) { } ``` </Tab> </Tabs> <Warning> Before deleting the privacy list, you should decline it. </Warning> ## Blocked user attempts to communicate with user A user can be blocked in **1-1 dialog** and **group dialog**. In this case, the blocked user receives an error when trying to send a message in a **1-1 dialog** and receives nothing when trying to send a message in **group dialog**. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setBody("I am blocked. I am trying to send message to you"); try { privateDialog.sendMessage(chatMessage); } catch (SmackException.NotConnectedException exception) { exception.printStackTrace(); } privateDialog.addMessageListener(new QBChatDialogMessageListener() { @Override public void processMessage(String dialogId, QBChatMessage chatMessage, Integer integer) { // when we received a message from dialog } @Override public void processError(String dialogId, QBChatException exception, QBChatMessage chatMessage, Integer senderId) { // when we received an error from dialog exception.printStackTrace(); } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val chatMessage = QBChatMessage() chatMessage.setBody("I am blocked. I am trying to send message to you") try { privateDialog.sendMessage(chatMessage) } catch (exception: SmackException.NotConnectedException) { exception.printStackTrace() } privateDialog.addMessageListener(object : QBChatDialogMessageListener() { override fun processMessage(dialogId: String?, chatMessage: QBChatMessage?, integer: Int?) { // when we received a message from dialog } override fun processError(dialogId: String?, exception: QBChatException?, chatMessage: QBChatMessage?, senderId: Int?) { // when we received an error from dialog exception?.printStackTrace() } }) ``` </Tab> </Tabs> # User Presence Source: https://docs.quickblox.com/sdks/android-chat-user-presence Learn how to track user presence updates and check user status using ping. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/android-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Subscribe to contact presence updates You can listen to the contact presence updates in real-time using the `QBRosterListener`. However, you can listen to the presence of only those users who have been added to the contact list. See [this section](/sdks/android-chat-contact-list) to learn how to implement the Contact List. <Tabs> <Tab title="Java"> ```Java theme={null} // you should do this after login (connect) to the Chat final QBRoster contactsRoster = QBChatService.getInstance().getRoster(); QBRosterListener rosterListener = new QBRosterListener() { @Override public void entriesDeleted(Collection<Integer> userIds) { } @Override public void entriesAdded(Collection<Integer> userIds) { } @Override public void entriesUpdated(Collection<Integer> userIds) { } @Override public void presenceChanged(QBPresence presence) { if (presence == null) { // no user in your contact list return; } // if a user uses several devices, you need to do additional check for presence QBPresence qbPresence = contactsRoster.getPresence(presence.getUserId()); if (qbPresence.getType() == QBPresence.Type.online) { // user is online } else { // user is offline } } }; contactsRoster.setSubscriptionMode(QBRoster.SubscriptionMode.mutual); contactsRoster.addRosterListener(rosterListener); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // you should do this after login (connect) to the Chat val contactsRoster = QBChatService.getInstance().roster val rosterListener: QBRosterListener = object : QBRosterListener { override fun entriesDeleted(userIds: Collection<Int>) { } override fun entriesAdded(userIds: Collection<Int>) { } override fun entriesUpdated(userIds: Collection<Int>) { } override fun presenceChanged(presence: QBPresence) { if (presence == null) { // no user in your contact list return } // if a user uses several devices, you need to do additional check for presence val qbPresence = contactsRoster.getPresence(presence.userId) if (qbPresence.type == QBPresence.Type.online) { // user is online } else { // user is offline } } } contactsRoster.subscriptionMode = QBRoster.SubscriptionMode.mutual contactsRoster.addRosterListener(rosterListener) ``` </Tab> </Tabs> ## Ping user QuickBlox SDK can send application-level pings to a user. As a result, you can check if the user is connected to the Chat server. <Tabs> <Tab title="Java"> ```Java theme={null} QBPingManager pingManager = QBChatService.getInstance().getPingManager(); Integer userId = 6453; pingManager.pingUser(userId, new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val pingManager = QBChatService.getInstance().pingManager val userId = 6453 pingManager.pingUser(userId, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void, bundle: Bundle) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | --------------- | | userID | yes | ID of the user. | ## Ping server QuickBlox SDK can send application-level pings to a server. As a result, you can check if there is a connection with the Chat server. <Tabs> <Tab title="Java"> ```Java theme={null} QBPingManager pingManager = QBChatService.getInstance().getPingManager(); pingManager.pingServer(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val pingManager = QBChatService.getInstance().pingManager pingManager.pingServer(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void, bundle: Bundle) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> ## Add ping listener Your pings can be unsuccessful. To handle unsuccessful pings, use the `PingFailedListener`. It enables your app to listen to failed ping attempts. <Tabs> <Tab title="Java"> ```Java theme={null} QBPingManager pingManager = QBChatService.getInstance().getPingManager(); PingFailedListener pingFailedListener = new PingFailedListener() { @Override public void pingFailed() { } }; // add PingManager when you need it pingManager.addPingFailedListener(pingFailedListener); // remove PingManager when you no longer need it pingManager.removePingFailedListener(pingFailedListener); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val pingManager = QBChatService.getInstance().pingManager val pingFailedListener = PingFailedListener { } // Add PingManager when you need it pingManager.addPingFailedListener(pingFailedListener) // Remove PingManager when you no longer need it pingManager.removePingFailedListener(pingFailedListener) ``` </Tab> </Tabs> # Content Source: https://docs.quickblox.com/sdks/android-content Learn how to store and access files with QuickBlox file storage. The content module allows storing rich chat attachments, app content, and settings without having to republish them. Using a web interface you or your clients can control and make instant changes to the apps. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. ## Retrieve files To retrieve the list of **own** files, you can use code below. <Tabs> <Tab title="Java"> ```Java theme={null} // for single QBFile QBContent.getFile(file).performAsync(new QBEntityCallback<QBFile>() { @Override public void onSuccess(QBFile file, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); // or you can get a list of QBFiles: QBPagedRequestBuilder requestBuilder = new QBPagedRequestBuilder(); requestBuilder.setPage(1); requestBuilder.setPerPage(10); QBContent.getFiles(requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBFile>>() { @Override public void onSuccess(ArrayList<QBFile> files, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // for single QBFile QBContent.getFile(file).performAsync(object : QBEntityCallback<QBFile> { override fun onSuccess(file: QBFile?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) // or you can get a list of QBFiles: val requestBuilder = QBPagedRequestBuilder() requestBuilder.page = 1 requestBuilder.perPage = 10 QBContent.getFiles(requestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBFile>> { override fun onSuccess(files: ArrayList<QBFile>?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Upload file You can upload a file to the cloud storage using the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} File file = new File("pathname"); boolean publicAccess = false; String tags = "dev, admin, birthday"; QBContent.uploadFileTask(file, publicAccess, tags, new QBProgressCallback() { @Override public void onProgressUpdate(int progress) { } }).performAsync(new QBEntityCallback<QBFile>() { @Override public void onSuccess(QBFile file, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val file: File = File("pathname") val publicAccess: Boolean = false val tags: String = "dev, admin, birthday" QBContent.uploadFileTask(file, publicAccess, tags, object : QBProgressCallback { override fun onProgressUpdate(progress: Int) { } }).performAsync(object : QBEntityCallback<QBFile> { override fun onSuccess(file: QBFile?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | ---------------- | -------- | ----------------------------------------------------------------------------------------------------- | | file | yes | File in the local storage. | | publicAccess | yes | File visibility. If the file is **public** then it's possible to download it without a session token. | | tags | yes | Comma-separated string with tags. | | progressCallback | no | Callback to receive upload progress. | The maximum size of the uploaded file depends on the membership plan. | Basic | Startup | Growth | HIPAA | Enterprise | | | --------------- | ------- | ------ | ----- | ---------- | --------------------------------------------------------------- | | File size limit | 10 Mb | 25 Mb | 50Mb | 50Mb | [Contact our sales team](https://quickblox.com/enterprise/#get) | ## Update file It's also possible to update the file's data without creating a new content record. <Tabs> <Tab title="Java"> ```Java theme={null} File newFile = new File("pathname"); Integer fileId = file.getId(); String tags = "dev, admin, birthday"; QBContent.updateFileTask(newFile, fileId, tags, new QBProgressCallback() { @Override public void onProgressUpdate(int progress) { } }).performAsync(new QBEntityCallback<QBFile>() { @Override public void onSuccess(QBFile file, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val newFile: File = File("pathname") val qbFileID: Int = file.id val tags: String = "dev, admin, birthday" QBContent.updateFileTask(newFile, fileID, tags, object : QBProgressCallback { override fun onProgressUpdate(progress: Int) { } }).performAsync(object : QBEntityCallback<QBFile> { override fun onSuccess(file: QBFile?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Download file by UID If the file is public then it's possible to download it without a session token. <Tabs> <Tab title="Java"> ```Java theme={null} String fileUID = file.getUid(); QBContent.downloadFile(fileUID).performAsync(new QBEntityCallback<InputStream>() { @Override public void onSuccess(InputStream inputStream, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val fileUID: String = file.uid QBContent.downloadFile(fileUID).performAsync(object : QBEntityCallback<InputStream> { override fun onSuccess(inputStream: InputStream?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Get file info Get information about a file by ID using the `getFile()` method. This method allows to load the file description model from the server, not the content of the file itself. <Tabs> <Tab title="Java"> ```Java theme={null} int fileId = 8192; QBContent.getFile(fileId).performAsync(new QBEntityCallback<QBFile>() { @Override public void onSuccess(QBFile file, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val fileId = 8192 QBContent.getFile(fileId).performAsync(object : QBEntityCallback<QBFile> { override fun onSuccess(file: QBFile, bundle: Bundle) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | --------------- | | fileID | yes | ID of the file. | ## Get file URL Except for downloading, you can also get the URL of each `QBFile`. There are two types of file URLs that can be obtained: private and public. * **Public URL** allows anyone to access the file, no authorization token is needed. * **Private URL** can be accessed only by QuickBlox user with a session token. ### Get public URL To receive a public URL, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} String publicUrl = QBFile.getPublicUrlForUID(fileUid); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val publicUrl = QBFile.getPublicUrlForUID(fileUid) ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | ------------------------------------------------------------------------------------------ | | fileUID | yes | File unique identifier. You can take the fileUid from the file using the getFile() method. | ### Get private URL To get a private URL of the uploaded file, use the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} String privateUrl = QBFile.getPrivateUrlForUID(fileUid); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val privateUrl = QBFile.getPrivateUrlForUID(fileUid) ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | ------------------------------------------------------------------------------------------ | | fileUid | yes | File unique identifier. You can take the fileUid from the file using the getFile() method. | ## Delete file Delete a file by file ID using the `deleteFile()` method below. <Tabs> <Tab title="Java"> ```Java theme={null} int fileId = 8192; QBContent.deleteFile(fileId).performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val fileId = 8192 QBContent.deleteFile(fileID).performAsync(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void, bundle: Bundle) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | --------------- | | fileID | yes | ID of the file. | # Custom Objects Source: https://docs.quickblox.com/sdks/android-custom-objects Learn how to store and sync data with QuickBlox key-value storage. Custom Objects module provides flexibility to define any data structure (schema) you need, build one-to-many relations between schemas and control permissions for all operations made on data. Schema is defined in QuickBlox Dashboard. There are two key concepts in Custom Objects: * **Class** represents your schema and contains field names and types. * **Record** represents the data you put into your schema. **Class** and **Record** are similar to table and row in relational database. Every class in Custom Object module comes with five mandatory predefined fields: `_id`, `user_id`, `parent_id`, `created_at`, and `updated_at`. Allowed data types: **Integer** (or Array of Integer); **String** (or Array of String); **Float** (or Array of Float); **Boolean** (or Array of Boolean); **Location** (Array of \[\< longitude >, \< latitude >]); **File**; **Date**. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. ## Create class <Note> Make sure that you have already added the appropriate dependency to your **app-level build.gradle file**. Review [this section](/sdks/android-setup#section-install-quick-blox-sdk-into-your-app) for more details. </Note> To start using Custom Objects module, create a class: 1. Go to [QuickBlox Dashboard](https://admin.quickblox.com). 2. Follow **Custom => Add => Add new class** direction. As a result, **Add new class** popup will appear. 3. Enter a class name, add any fields you want. <img alt="1454" /> 4. Click **Create class button** to create a new class. <img alt="1690" /> ## Create records The easiest way to create a new record from the [QuickBlox Dashboard](https://admin.quickblox.com/signin), do the following: 1. Follow **Custom => Current class => Your Class** direction. 2. Click **Add => Add record** button and **Add new record** popup will appear. 3. Fill in any fields you want. 4. Click **Add record button** and a new record will be added and shown in the table. To create a single object, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObject customObject = new QBCustomObject(); // put fields customObject.putString("text", "Lorem ipsum dolor sit amen"); customObject.putInteger("version", 7); customObject.putArray("tags", Arrays.asList("#test", "#customobjects", "#quickblox-android-sdk")); customObject.putArray("friendsIds", Arrays.asList(12345, 12346, 12347)); // set the class name customObject.setClassName("CustomObject2"); QBCustomObjects.createObject(customObject).performAsync(new QBEntityCallback<QBCustomObject>() { @Override public void onSuccess(QBCustomObject createdObject, Bundle params) { // object created successfully } @Override public void onError(QBResponseException exception) { // error occurred } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val customObject = QBCustomObject() // put fields customObject.putString("text", "Lorem ipsum dolor sit amen") customObject.putInteger("version", 7) customObject.putArray("tags", arrayListOf("#test", "#customobjects", "#quickblox-android-sdk")) customObject.putArray("friendsIds", arrayListOf(12345, 12346, 12347)) // set the class name customObject.className = "CustomObject2" QBCustomObjects.createObject(customObject).performAsync(object : QBEntityCallback<QBCustomObject> { override fun onSuccess(createdObject: QBCustomObject, params: Bundle) { // object created successfully } override fun onError(exception: QBResponseException) { // error occured } }) ``` </Tab> </Tabs> To create multiple objects, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObject customObject1 = new QBCustomObject(); customObject1.putString("text", "Lorem ipsum dolor sit amen"); customObject1.putInteger("version", 1000); customObject1.putArray("tags", Arrays.asList("#test", "#customobjects", "#quickblox-android-sdk")); customObject1.putArray("friendsIds", Arrays.asList(12345, 12346, 12347)); customObject1.setClassName("CustomObject2"); QBCustomObject customObject2 = new QBCustomObject(); customObject2.putString("text", "Lorem ipsum dolor sit amen"); customObject2.putInteger("version", 1001); customObject2.putArray("tags", Arrays.asList("#test", "#customobjects", "#quickblox-android-sdk")); customObject2.setClassName("CustomObject2"); List<QBCustomObject> customObjects = new ArrayList<>(); customObjects.add(customObject1); customObjects.add(customObject2); QBCustomObjects.createObjects(customObjects).performAsync(new QBEntityCallback<ArrayList<QBCustomObject>>() { @Override public void onSuccess(ArrayList<QBCustomObject> customObjects, Bundle bundle) { // new records created } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val customObject1 = QBCustomObject() customObject1.putString("text", "Lorem ipsum dolor sit amen") customObject1.putInteger("version", 1000) customObject1.putArray("tags", Arrays.asList("#test", "#customobjects", "#quickblox-android-sdk")) customObject1.putArray("friendsIds", Arrays.asList(12345, 12346, 12347)) customObject1.className = "CustomObject2" val customObject2 = QBCustomObject() customObject2.putString("text", "Lorem ipsum dolor sit amen") customObject2.putInteger("version", 1001) customObject2.putArray("tags", Arrays.asList("#test", "#customobjects", "#quickblox-android-sdk")) customObject2.className = "CustomObject2" val customObjects = ArrayList<QBCustomObject>() customObjects.add(customObject1) customObjects.add(customObject2) QBCustomObjects.createObjects(customObjects).performAsync(object : QBEntityCallback<ArrayList<QBCustomObject>> { override fun onSuccess(customObjects: ArrayList<QBCustomObject>, bundle: Bundle) { // new records created } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> <Warning> Make sure that `className` is the same in all objects. The current Class should be created in the Admin Panel previously. You can create multiple items for a **single** custom class. The name will be taken from the first item in the list. </Warning> ## Retrieve records by IDs To get records with a particular record ID, use the `getObjectsByIds()` method. Set the record ID using the ` coIDs` object. Go over [Sort operators](/sdks/android-custom-objects#sort-operators) and [Search operators](/sdks/android-custom-objects#search-operators) sections to learn about filters and search operators you can use to retrieve records. <Tabs> <Tab title="Java"> ```Java theme={null} StringifyArrayList<String> coIds = new StringifyArrayList<String>(); coIds.add("50e67e6e535c121c66004c74"); coIds.add("50e67e6d535c127f66004f47"); coIds.add("50e67e6b535c121c66004c72"); coIds.add("50e59f81535c121c660015fd"); QBCustomObjects.getObjectsByIds("Note", coIds).performAsync(new QBEntityCallback<ArrayList<QBCustomObject>>() { @Override public void onSuccess(ArrayList<QBCustomObject> customObjects, Bundle bundle) { // records retrieved } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val coIds = StringifyArrayList<String>() coIds.add("50e67e6e535c121c66004c74") coIds.add("50e67e6d535c127f66004f47") coIds.add("50e67e6b535c121c66004c72") coIds.add("50e59f81535c121c660015fd") QBCustomObjects.getObjectsByIds("Note", coIds).performAsync(object : QBEntityCallback<ArrayList<QBCustomObject>> { override fun onSuccess(customObjects: ArrayList<QBCustomObject>, bundle: Bundle) { // records retrieved } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> <Tab title="JavaScript"> ```JavaScript theme={null} var className = "Note", filter = { '_id': { 'in' ['5f59b10fa0eb4772bd5e9976','5f59a76ca28f9a28032944d3'] } }; QB.data.list(className, filter, function(err, result) { if (err) { } else { } }); ``` </Tab> </Tabs> ## Retrieve records You can search for records of a particular class. The request below will return all records of the `Movie` class with the `rating` value greater than `5.5`, sorted in ascending order, and limited to 5 records on the page. <Tabs> <Tab title="Java"> ```Java theme={null} String className = "Movie"; QBRequestGetBuilder requestBuilder = new QBRequestGetBuilder(); requestBuilder.gt("rating", "5.5"); requestBuilder.setLimit(5); requestBuilder.sortAsc("rating"); QBCustomObjects.getObjects(className, requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBCustomObject>>() { @Override public void onSuccess(ArrayList<QBCustomObject> customObjects, Bundle bundle) { // objects Successfully Loaded } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val className = "Movie" val requestBuilder = QBRequestGetBuilder() requestBuilder.gt("rating", "5.5") requestBuilder.limit = 5 requestBuilder.sortAsc("rating") QBCustomObjects.getObjects(className, requestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBCustomObject>> { override fun onSuccess(customObjects: ArrayList<QBCustomObject>, bundle: Bundle) { // objects Successfully Loaded } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------------- | -------- | ----------------------------------------- | | className | yes | Name of a custom object class. | | requestBuilder | yes | Allows to set parameters for the request. | If you want to retrieve only records updated after some specific date time and order the search results, you can apply operators. Thus, you can apply [search](/sdks/android-custom-objects#search-operators) and [sort](/sdks/android-custom-objects#sort-operators) operators to the list of records on the page so that it is easier to view specific records. The operators are set in the `QBRequestGetBuilder` class. If you want to get a paginated list of users from the server, you can set the following pagination parameters in the `QBRequestGetBuilder` class. | Pagination parameter | Required | Description | | -------------------- | -------- | ------------------------------------------------------------------------------------------- | | skip | no | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | no | Limit search results to N records. Useful for pagination. Default value: **100**. | ### Search operators You can use search operators to get more specific search results. The request below will return records of the `Movie` class with the `rating` value greater than `5.5`. <Tabs> <Tab title="Java"> ```Java theme={null} String className = "Movie"; QBRequestGetBuilder requestBuilder = new QBRequestGetBuilder(); requestBuilder.gt("rating", "5.5"); QBCustomObjects.getObjects(className, requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBCustomObject>>() { @Override public void onSuccess(ArrayList<QBCustomObject> customObjects, Bundle bundle) { // objects successfully loaded } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val className = "Movie" val requestBuilder = QBRequestGetBuilder() requestBuilder.gt("rating", "5.5") QBCustomObjects.getObjects(className, requestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBCustomObject>> { override fun onSuccess(customObjects: ArrayList<QBCustomObject>, bundle: Bundle) { // objects Successfully Loaded } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Description | | ---------------- | ------------------------------- | ---------------------------------------------------- | | `lt` | integer, float | **Less Than** operator. | | `lte` | integer, float | **Less Than** or **Equal** to operator. | | `gt` | integer, float | **Greater Than** operator. | | `gte` | integer, float | **Greater Than** or **Equal** to operator. | | `ne` | integer, float, string, boolean | **Not Equal** to operator. | | `in` | integer, float, string | **IN** array operator. | | `nin` | integer, float, string | Not **IN** array operator. | | `all` | array | **ALL** are contained in array. | | `or` | integer, float, string | All records that contain a value 1 **or** value 2. | | `ctn` | string | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return records of the `Movie` class by the `rating` sorted in ascending order. <Tabs> <Tab title="Java"> ```Java theme={null} String className = "Movie"; QBRequestGetBuilder requestBuilder = new QBRequestGetBuilder(); requestBuilder.sortAsc("rating"); QBCustomObjects.getObjects(className, requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBCustomObject>>() { @Override public void onSuccess(ArrayList<QBCustomObject> customObjects, Bundle bundle) { // objects successfully loaded } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val className = "Movie" val requestBuilder = QBRequestGetBuilder() requestBuilder.sortAsc("rating") QBCustomObjects.getObjects(className, requestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBCustomObject>> { override fun onSuccess(customObjects: ArrayList<QBCustomObject>, bundle: Bundle) { // objects Successfully Loaded } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> Here are the operators that you can use to order the search results. | Sort operator | Applicable to types | Description | | ------------- | ------------------- | ------------------------------------------------------------------------- | | sortAsc | All types | Search results will be sorted in ascending order by the specified field. | | sortDesc | All types | Search results will be sorted in descending order by the specified field. | ## Get a number of records You can get a number of records using the `countObjects()` method. The request below will return a count of records of the `Movie` class with the `rating` value greater than `5.5`. <Tabs> <Tab title="Java"> ```Java theme={null} String className = "Movie"; QBRequestGetBuilder requestBuilder = new QBRequestGetBuilder(); requestBuilder.gt("rating", "5.5"); QBCustomObjects.countObjects(className, requestBuilder).performAsync(new QBEntityCallback<Integer>() { @Override public void onSuccess(Integer count, Bundle bundle) { // objects successfully loaded } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val className = "Movie" val requestBuilder = QBRequestGetBuilder() requestBuilder.gt("rating", "5.5") QBCustomObjects.countObjects(className, requestBuilder).performAsync(object : QBEntityCallback<Int> { override fun onSuccess(count: Int, bundle: Bundle) { // objects Successfully Loaded } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------------- | -------- | ----------------------------------------- | | className | yes | Name of a custom object class. | | requestBuilder | yes | Allows to set parameters for the request. | ## Update records You can update a single record. You should have the name of the class and the `CustomObjectId` to update the object. To update your Custom Objects, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObject record = new QBCustomObject(); record.setClassName("Movie"); HashMap<String, Object> fields = new HashMap<>(); fields.put("rating", "7.88"); record.setFields(fields); record.setCustomObjectId("502f7c4036c9ae2163000002"); QBCustomObjects.updateObject(record).performAsync(new QBEntityCallback<QBCustomObject>() { @Override public void onSuccess(QBCustomObject customObject, Bundle bundle) { // record updated } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val record = QBCustomObject() record.className = "Movie" val fields = HashMap<String, Any>() fields.put("rating", "7.88") record.fields = fields record.customObjectId = "502f7c4036c9ae2163000002" QBCustomObjects.updateObject(record).performAsync(object : QBEntityCallback<QBCustomObject> { override fun onSuccess(customObject: QBCustomObject, bundle: Bundle) { // record updated } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> You can update multiple records using the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObject customObject1 = new QBCustomObject("Note"); customObject1.putInteger("rating", 10); customObject1.setCustomObjectId("50e3f85f535c123376000d32"); QBCustomObject customObject2 = new QBCustomObject("Note"); customObject2.putInteger("rating", 8); customObject2.setCustomObjectId("50e3f85f535c123376000d31"); QBCustomObject customObject3 = new QBCustomObject("Note"); customObject3.putInteger("rating", 12); customObject3.setCustomObjectId("50e3f85f535c123376000d30"); List<QBCustomObject> customObjectList = new LinkedList<>(); customObjectList.add(customObject1); customObjectList.add(customObject2); customObjectList.add(customObject3); QBCustomObjects.updateObjects(customObjectList).performAsync(new QBEntityCallback<ArrayList<QBCustomObject>>() { @Override public void onSuccess(ArrayList<QBCustomObject> objects, Bundle bundle) { // records updated } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val customObject1 = QBCustomObject("Note") customObject1.putInteger("rating", 10) customObject1.customObjectId = "50e3f85f535c123376000d32" val customObject2 = QBCustomObject("Note") customObject2.putInteger("rating", 8) customObject2.customObjectId = "50e3f85f535c123376000d31" val customObject3 = QBCustomObject("Note") customObject3.putInteger("rating", 12) customObject3.customObjectId = "50e3f85f535c123376000d30" val customObjectList = LinkedList<QBCustomObject>() customObjectList.add(customObject1) customObjectList.add(customObject2) customObjectList.add(customObject3) QBCustomObjects.updateObjects(customObjectList).performAsync(object : QBEntityCallback<ArrayList<QBCustomObject>> { override fun onSuccess(objects: ArrayList<QBCustomObject>, bundle: Bundle) { // records updated } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> ## Delete records To delete a single record, use the code snippet below. You should know the name of the class and the `CustomObjectId`. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObject customObject = new QBCustomObject("Movie", "502f83ed36c9aefa62000002"); QBCustomObjects.deleteObject(customObject).performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { // record deleted } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val customObject = QBCustomObject("Movie", "502f83ed36c9aefa62000002") QBCustomObjects.deleteObject(customObject).performAsync(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void, bundle: Bundle) { // record deleted } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> To delete multiple records, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} StringifyArrayList<String> deleteIds = new StringifyArrayList<>(); deleteIds.add("50e3f85f535c123376000d31"); deleteIds.add("50e3f85f535c123376000d32"); QBCustomObjects.deleteObjects("Note", deleteIds).performAsync(new QBEntityCallback<ArrayList<String>>() { @Override public void onSuccess(ArrayList<String> strings, Bundle bundle) { // records deleted } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val deleteIds = StringifyArrayList<String>() deleteIds.add("50e3f85f535c123376000d31") deleteIds.add("50e3f85f535c123376000d32") QBCustomObjects.deleteObjects("Note", deleteIds).performAsync(object : QBEntityCallback<ArrayList<String>> { override fun onSuccess(strings: ArrayList<String>, bundle: Bundle) { // Records deleted } override fun onError(errors: QBResponseException) { // Handle error } }) ``` </Tab> </Tabs> ## Relations It is possible to create a relation between objects **of two different classes** via `_parent_id` field. For example, we have the class **Rating** that contains `score`, `review`, and `comment` fields. We also have a **Movie** class. So we can create a record of class **Rating** that will point to the record of the class **Movie** via its `_parent_id` field, so the `_parent_id` field will contain the ID of record from class **Movie**. <Warning> This is not a simple soft link. This is actually a **hard link**. When you delete the **Movie** class record then all its children (records of class **Rating** with `_parent_id` field set to the **Movie** class record ID) will be automatically **deleted** as well. </Warning> <Note> If you need to retrieve all children, you can retrieve records with the filter `_parent_id=<id_of_parent_class_record>`. </Note> ## Permissions Access Control List (ACL) is a list of permissions attached to some object. An ACL specifies which users have access to objects as well as what operations are allowed on given objects. Each entry in a typical ACL specifies a subject and an operation. ACL models may be applied to collections of objects as well as to individual entities within the system hierarchy. <Note> Access Control List available **only** for Custom Objects module. </Note> ### Permission schema QuickBlox Permission schema contains **five** permissions levels: * **Open (open)** Such permission schema means that any user within the application can access the record/records in the class and is allowed to perform an action with the current permission level. * **Owner (owner)** Owner permission level means that only Owner (a user who created a record) is allowed to perform action with the current permission level. * **Not allowed (not\_allowed)** No one (except for the Account Administrator) can make a chosen action. * **Open for groups (open\_for\_groups)** Users having a specified tag/tags (see more info about how to set tags for the user in [Users](/sdks/android-users) section) will be included in the group that is allowed to perform an action with the current permission level. The current permission level can consist of one or several groups (number of groups is **not** limited). Tags can be added/deleted in the user’s profile. * **Open for user ids (open\_for\_users\_ids)** Only users that are specified in the permission level can make a required action with a record. One or several users can be specified (the number of users is **not** limited). ### Actions available for the entity * **Create** Create a record. * **Read** Retrieve and read the info about the chosen record. * **Update** Update any parameter for the chosen record (only those parameters that can be set by the user can be updated). * **Delete** Delete a record. ### Permission levels There are two access levels in the Permissions schema: **Class** and **Record**. #### Class entity Only the Account Administrator can create a class in the Custom object module and make all possible actions with it. Operations with **Class** entity are not allowed in API. All actions (Create, Read, Update, and Delete) are available for the class entity and are applicable for all records in the class. Every action has a separate permission level available. The exception is a Create action that is not available for the **Owner** permission level. To set a permission schema for the Class, do the following: 1. Go to the **Custom Objects** tab. 2. Open a required class. 3. Click **Edit permissions** button to open a class and edit it. <img alt="1690" /> Default Class permission schema is used while creating a class: * **Create**: Open * **Read**: Open * **Update**: Owner * **Delete**: Owner <Note> Mark checkboxes to **enable** class permissions. </Note> #### Record entity A record is an entity within the class in the Custom Objects module that has its own permission levels. You can create a record in the Dashboard and API (see [Create Record](#section-create-records) request for more details). All permission levels except for the **Not Allowed** are available for the record and there are only three actions available and applicable for the record: Read, Update, and Delete. Default Record permission schema is used while creating a class: * **Read**: Open * **Update**: Owner * **Delete**: Owner To set a permission level open the required Class and click the record to edit it. <Frame> <img alt="android-custom-edit-record.png" /> </Frame> ### Choosing a permission schema Only **one** permission level can be applicable to the record: class permission schema or record permission schema. To apply class permission levels to **all** records in the class tick the checkbox in the **Use Class permissions** column near the required Action in the Dashboard. <Frame> <img alt="android-custom-edit-permissions.png" /> </Frame> <Note> Using a class permission schema means that a record permission schema will **not** affect a reсord. </Note> <Tip> In case, the Admin **does not** tick the checkbox in the Dashboard a user **has** a possibility to change permission levels for every separate record in the table or create a new one with the ACL that a user requires. </Tip> ### Create record with permissions Let's create a record with the next permissions: * READ: Open. * UPDATE: Users in groups **golf**, **man**. * DELETE: Users with IDs **3060**, **63635**. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObject newRecord = new QBCustomObject("Note"); newRecord.put("rating", 99); newRecord.put("description", "Hello world"); // // set permissions: // READ QBPermissions permissions = new QBPermissions(); permissions.setReadPermission(QBPermissionsLevel.OPEN); // // UPDATE ArrayList<String> usersTags = new ArrayList<String>(); usersTags.add("golf"); usersTags.add("man"); permissions.setDeletePermission(QBPermissionsLevel.OPEN_FOR_GROUPS, usersTags); // // DELETE ArrayList<String> usersIdS = new ArrayList<String>(); usersIdS.add("0602"); usersIdS.add("1986"); permissions.setDeletePermission(QBPermissionsLevel.OPEN_FOR_USER_IDS, usersIdS); newRecord.setPermission(permissions); QBCustomObjects.createObject(newRecord).performAsync(new QBEntityCallback<QBCustomObject>() { @Override public void onSuccess(QBCustomObject customObject, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val newRecord = QBCustomObject("Note") newRecord.put("rating", 99) newRecord.put("description", "Hello world") // // set permissions: // READ val permissions = QBPermissions() permissions.setReadPermission(QBPermissionsLevel.OPEN) // // UPDATE val usersTags = ArrayList<String>() usersTags.add("golf") usersTags.add("man") permissions.setDeletePermission(QBPermissionsLevel.OPEN_FOR_GROUPS, usersTags) // // DELETE val usersIdS = ArrayList<String>() usersIdS.add("0602") usersIdS.add("1986") permissions.setDeletePermission(QBPermissionsLevel.OPEN_FOR_USER_IDS, usersIdS) newRecord.permission = permissions QBCustomObjects.createObject(newRecord).performAsync(object : QBEntityCallback<QBCustomObject> { override fun onSuccess(customObject: QBCustomObject?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ### Retrieve record permissions You can obtain info about record permissions by its ID. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObjects.getObjectPermissions("Note", "53f44e7befa3573473000002").performAsync(new QBEntityCallback<QBPermissions>() { @Override public void onSuccess(QBPermissions permissions, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBCustomObjects.getObjectPermissions("Note", "53f44e7befa3573473000002").performAsync(object : QBEntityCallback<QBPermissions> { override fun onSuccess(permissions: QBPermissions?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> <Warning> **Only** info about the user's own records is available. </Warning> ### Update record permissions Let's update record's permissions to next: * READ: Users in groups **car, developers**. * UPDATE: Owner. * DELETE: Owner. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObject record = new QBCustomObject(); record.setClassName("Note"); record.setCustomObjectId("52b30274535c12fbf80121bd"); // // update permissions: // READ QBPermissions permissions = new QBPermissions(); ArrayList<String> usersTags = new ArrayList<String>(); usersTags.add("car"); usersTags.add("developers"); permissions.setDeletePermission(QBPermissionsLevel.OPEN_FOR_GROUPS, usersTags); // // UPDATE permissions.setUpdatePermission(QBPermissionsLevel.OWNER); // // DELETE permissions.setDeletePermission(QBPermissionsLevel.OWNER); record.setPermission(permissions); QBCustomObjects.updateObject(record).performAsync(new QBEntityCallback<QBCustomObject>() { @Override public void onSuccess(QBCustomObject customObject, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val record = QBCustomObject() record.className = "Note" record.customObjectId = "52b30274535c12fbf80121bd" // // update permissions: // READ val permissions = QBPermissions() val usersTags = ArrayList<String>() usersTags.add("car") usersTags.add("developers") permissions.setDeletePermission(QBPermissionsLevel.OPEN_FOR_GROUPS, usersTags) // // UPDATE permissions.setUpdatePermission(QBPermissionsLevel.OWNER) // // DELETE permissions.setDeletePermission(QBPermissionsLevel.OWNER) record.permission = permissions QBCustomObjects.updateObject(record).performAsync(object : QBEntityCallback<QBCustomObject> { override fun onSuccess(customObject: QBCustomObject?, bundle: Bundle?) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> ## Files Custom Objects module supports the `File` field type. It is created to work easily with content from the Custom Objects module. There is an ability to upload, download, update, and delete the content of file fields. ## Upload/Update file Use the code lines below to upload/update a file. <Tabs> <Tab title="Java"> ```Java theme={null} // get file File file = new File(); QBCustomObject customObject = new QBCustomObject("Note", "p38a06v6l123o50v66"); QBCustomObjectsFiles.uploadFile(file, customObject, "avatar", new QBProgressCallback() { @Override public void onProgressUpdate(int progress) { } }).performAsync(new QBEntityCallback<QBCustomObjectFileField>() { @Override public void onSuccess(QBCustomObjectFileField customObjectFileField, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // get file val file = ... val customObject = QBCustomObject("Note", "p38a06v6l123o50v66") QBCustomObjectsFiles.uploadFile(file, customObject, "avatar", object : QBProgressCallback { override fun onProgressUpdate(progress: Int) { } }).performAsync(object : QBEntityCallback<QBCustomObjectFileField> { override fun onSuccess(customObjectFileField: QBCustomObjectFileField?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Download file To download a file, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObject customObject = new QBCustomObject("Note", "p38a06v6l123o50v66"); QBCustomObjectsFiles.downloadFile(customObject, "avatar", new QBProgressCallback() { @Override public void onProgressUpdate(int progress) { } }).performAsync(new QBEntityCallback<InputStream>() { @Override public void onSuccess(InputStream inputStream, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val customObject = QBCustomObject("Note", "p38a06v6l123o50v66") QBCustomObjectsFiles.downloadFile(customObject, "avatar", object : QBProgressCallback { override fun onProgressUpdate(progress: Int) { } }).performAsync(object : QBEntityCallback<InputStream> { override fun onSuccess(inputStream: InputStream?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Delete file To delete a file, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBCustomObject customObject = new QBCustomObject("Note", "p38a06v6l123o50v66"); QBCustomObjectsFiles.deleteFile(customObject, "avatar").performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val customObject = QBCustomObject("Note", "p38a06v6l123o50v66") QBCustomObjectsFiles.deleteFile(customObject, "avatar").performAsync(object : QBEntityCallback<Void> { override fun onSuccess(void: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> # Push Notifications Source: https://docs.quickblox.com/sdks/android-push-notifications Learn how to send push notifications to users when they are offline. <Warning> Firebase is shutting down FCM legacy API on **June 20, 2024**. Migrate to FCM HTTP v1 API before this date in order to keep Android Push Notifications working. Follow steps in our [migration guide](https://quickblox.com/blog/firebase-cloud-messaging-fcm-migration-guide/). In case of any issues please reach us out via [our Help Center](https://help.quickblox.com/conversation/new). </Warning> Push Notifications provide a way to deliver some information to a user while they are not using your app actively. The following use cases can be covered by push notifications: * **Offline messages.** Send a chat message when a recipient is offline. In this case, a push notification will be sent automatically if the user is offline. * **Offline calls.** Make a video/audio call with offline opponents. In this case, a push notification will be sent manually. * **Requests to contact list.** Send requests to add a user to the contact list. In this case, a push notification will be sent manually). * **User tags.** Send notifications to specific user groups defined by tags. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. <Tip> HTTP v1 API it's recommended solution. </Tip> <AccordionGroup> <Accordion title="HTTP v1 API"> ### 1. Generate a new service account key <Frame> <img alt="Generate New Service Account Key" /> </Frame> To generate a new service account key, you need to: 1. Open the Firebase console and select your project. 2. Go to Project settings > Service Accounts. 3. Click on Generate new private key and confirm by clicking Generate key. 4. Securely store the JSON file containing the private key that will be downloaded ### 2. Navigate to the push notifications section on QuickBlox dashboard <Frame> <img alt="Navigate to Push Notifications Section on QuickBlox Dashboard" /> </Frame> To navigate to the push notifications section: 1. Head over to QuickBlox dashboard, and choose your application. 2. Select the push notifications tab and navigate to the settings page. 3. Select the Service account key tab Note: we will remove the Server key tab when it will be discontinued by FCM, you will by default only have the service account key section on the dashboard. ### 3. Upload the service account key on QuickBlox dashboard <Frame> <img alt="Upload Service Account Key on QuickBlox Dashboard" /> </Frame> To upload your service account key: 1. Choose the environment for your service account key. (Development/Production) 2. Click on the browse button and select the JSON file containing the key that was downloaded in step-1. 3. Hit the upload button. Note: When setting up your environment, it’s important to distinguish between development and production modes. If you’ve uploaded a development certificate, it will only function for subscriptions created in the development environment, and likewise for production. This separation ensures seamless testing and deployment of push notifications across different environments. <Warning> API (Legacy) it's deprecated, not recommended </Warning> </Accordion> <Accordion title="How to enable Cloud Messaging API (Legacy)"> To enable **Cloud Messaging API (legacy)** instead of **Firebase Cloud Messaging API (HTTP V1)**, please log in to your Firebase account, afterward select your Application and do the following: 1. Click on the Settings icon. 2. Select **Project Settings** from the drop-down list. <Frame> <img alt="Project Settings Cloud Messaging Tab" /> </Frame> 3. In section **Project Settings** select **Cloud Messaging** tab. 4. Click on the three-dots (kebab) menu to the right of **Cloud Messaging API (Legacy)** and choose the **Manage API in Google Cloud Console** option. You will be redirected to **Google Cloud Console**. <Frame> <img alt="Enable Cloud Messaging" /> </Frame> 5. Click the **Enable** buttons in the **Cloud Messaging** section. <Frame> <img alt="Server Key Appears" /> </Frame> 6. Refresh the **Cloud Messaging** page and a server key will appear. <Frame> <img alt="Configure Firebase Project and API Key" /> </Frame> </Accordion> </AccordionGroup> ## Configure Firebase project and API key To start working with push notifications functionality, you need to configure it. At first, you should create a Firebase account if you haven't it. Then you should configure your [Firebase project](https://console.firebase.google.com/) and obtain the **API key**. 1. To find your **FCM server key**, go to your **Firebase console => Project Settings => Cloud Messaging**. <Frame> <img alt="Firebase Project Settings" /> </Frame> <Frame> <img alt="Server Key" /> </Frame> 2. Copy the **server key** to your **Dashboard => *YOUR\_APP* => Push Notifications => Settings**, select the environment for which you are adding the key and click the **Save key**. Use the same server key for development and production zones. You can use both these environments up to you. <Frame> <img alt="Android Push Settings" /> </Frame> ## Add Firebase to your Project 1. As part of enabling Firebase services in your Android application, you need to add the **google-services dependency** to your **project-level** `build.gradle` file. ```Groovy Groovy theme={null} buildscript { // ... dependencies { // ... classpath 'com.google.gms:google-services:4.3.10' } } ``` 2. Add FCM dependency to your **app level** `build.gradle` file. ```Groovy Groovy theme={null} implementation "com.google.firebase:firebase-core:20.0.0" ``` 3. Include a **gms plugin** to your **app level** `build.gradle` file. ```Groovy Groovy theme={null} apply plugin: 'com.google.gms.google-services' ``` 4. Download the `google-services.json` file from your Firebase Project dashboard and put it into your app folder in your Android project. 5. Copy the **sender ID** value following **Firebase console => Project Settings => Cloud Messaging**. <Frame> <img alt="Locate Project Settings in Android" /> </Frame> <Frame> <img alt="Sender ID" /> </Frame> 6. Edit your app **AndroidManifest** file and add your Firebase **sender ID** as well as notification type and environment to integrate automatic push subscription. ```XML XML theme={null} <meta-data android:name="com.quickblox.messages.TYPE" android:value="FCM"/> <meta-data android:name="com.quickblox.messages.SENDER_ID" android:value="639872757929"/> <meta-data android:name="com.quickblox.messages.QB_ENVIRONMENT" android:value="DEVELOPMENT"/> ``` * `com.quickblox.messages.TYPE` - can be `GCM` or `FCM`. * `com.quickblox.messages.SENDER_ID` - your sender ID from google console (for example, 639872757929). * `com.quickblox.messages.QB_ENVIRONMENT` - can be `DEVELOPMENT` or `PRODUCTION`. 7. Then you need to setup `QBFcmPushListenerService` and `QBFcmPushInstanceIDService` in AndroidManifest. ```XML XML theme={null} <service android:name="com.quickblox.messages.services.fcm.QBFcmPushListenerService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT"/> </intent-filter> </service> <service android:name="com.quickblox.messages.services.fcm.QBFcmPushInstanceIDService"> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/> </intent-filter> </service> ``` ## Automatic push subscription QuickBlox Android SDK provides automatic push subscription management. It means that you do not need to bother how to get FCM device token, create push subscription, and what to do with the received data. Thus, you can reduce your code and make it cleaner. <Warning> A single user can have up to **10** subscriptions on different devices. </Warning> ### Enable/Disable push subscription Here you can use a global setting to enable or disable delivery of push notifications. Set this parameter only once. <Tabs> <Tab title="Java"> ```Java theme={null} QBSettings.getInstance().setEnablePushNotification(false); // by default is true boolean isEnabled = QBSettings.getInstance().isEnablePushNotification(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBSettings.getInstance().isEnablePushNotification = true // By Default is TRUE boolean isEnabled = QBSettings.getInstance().isEnablePushNotification ``` </Tab> </Tabs> ### Track subscription status To be aware of what is happening with your push subscription, whether you're subscribed successfully or not, you can use the `QBSubscribeListener`. Just add `QBSubscribeListener` right after the `QBSettings.getInstance().init()` code. <Tabs> <Tab title="Java"> ```Java theme={null} QBPushManager.getInstance().addListener(new QBPushManager.QBSubscribeListener() { @Override public void onSubscriptionCreated() { } @Override public void onSubscriptionError(Exception exception, int resultCode) { if (resultCode >= 0) { // might be Google play service exception } } @Override public void onSubscriptionDeleted(boolean delete) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBPushManager.getInstance().addListener(object : QBPushManager.QBSubscribeListener { override fun onSubscriptionCreated() { } override fun onSubscriptionError(exception: Exception?, resultCode: Int?) { if (resultCode >= 0) { // might be Google play service exception } } override fun onSubscriptionDeleted(deleted: Boolean?) { } }) ``` </Tab> </Tabs> ## Manual push subscription If you do not want to use the automatic push subscription feature, then do the following: 1. Set `SubscribePushStrategy.NEVER` as the main strategy. <Tabs> <Tab title="Java"> ```Java theme={null} // default SubscribePushStrategy.ALWAYS QBSettings.getInstance().setSubscribePushStrategy(SubscribePushStrategy.NEVER) ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // default SubscribePushStrategy.ALWAYS QBSettings.getInstance().subscribePushStrategy = SubscribePushStrategy.NEVER ``` </Tab> </Tabs> In this case, you need to subscribe and unsubscribe manually using the following methods: <Tabs> <Tab title="Java"> ```Java theme={null} SubscribeService.subscribeToPushes(context, false); SubscribeService.unSubscribeFromPushes(context); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} SubscribeService.subscribeToPushes(context, false) SubscribeService.unSubscribeFromPushes(context) ``` </Tab> </Tabs> 2. And then in your class extended from `QBFcmPushListenerService` you need to handle when the token is refreshed: <Tabs> <Tab title="Java"> ```Java theme={null} @Override public void onNewToken(String token) { boolean tokenRefreshed = true; SubscribeService.subscribeToPushes(context, tokenRefreshed); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} override fun onNewToken(token: String?) { val tokenRefreshed = true SubscribeService.subscribeToPushes(context, tokenRefreshed) } ``` </Tab> </Tabs> 3. Add your class extended from `QBFcmPushListenerService` to the Manifest file. ```XML XML theme={null} <service android:name="your_file_location.YourFilePushListenerService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT"/> </intent-filter> </service> <service android:name="com.quickblox.messages.services.fcm.QBFcmPushInstanceIDService"> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/> </intent-filter> </service> ``` <Warning> The `token` is a device registration token generated by the APNs or GCM/FCM. The token can be unregistered by the APNs or GCM/FCM anytime. In this case, the device should be registered again and obtain a new token. When a new token is obtained, a new subscription should be created. </Warning> ## Send push notifications You can manually initiate sending of push notifications to a user/users on any event in your application. To send a push notification, you should use `QBEvent`, fill its fields with push notification parameters (payload) and set push recipients. <Tabs> <Tab title="Java"> ```Java theme={null} StringifyArrayList<Integer> userIds = new StringifyArrayList<>(); for (QBUser user : userList) { userIds.add(user.getId()); } QBEvent event = new QBEvent(); event.setUserIds(userIDs); event.setEnvironment(QBEnvironment.DEVELOPMENT); event.setNotificationType(QBNotificationType.PUSH); event.setPushType(QBPushType.GCM); HashMap<String, Object> customData = new HashMap<>(); customData.put("data.message", "Hello QuickBlox"); customData.put("data.type", "First QuickBlox Push"); event.setMessage(customData); QBPushNotifications.createEvents(event).performAsync(new QBEntityCallback<QBEvent>() { @Override public void onSuccess(QBEvent event, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val userIds = StringifyArrayList<Int>() for (user in userList) { userIds.add(user.id) } val event = QBEvent() event.userIds = userIDs event.environment = QBEnvironment.DEVELOPMENT event.notificationType = QBNotificationType.PUSH event.pushType = QBPushType.GCM val customData = HashMap<String, Any>() customData["data.message"] = "Hello QuickBlox" customData["data.type"] = "First QuickBlox Push" event.setMessage(customData) QBPushNotifications.createEvent(event).performAsync(object : QBEntityCallback<QBEvent> { override fun onSuccess(event: QBEvent?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> <Warning> You can send only FCM data messages to the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages on your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notification. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> <Note> You can send APNs VoIP notifications to the iOS app. However, if the iOS app is not subscribed to APNs VoIP notifications or the APNs VoIP certificate has expired, the regular APNs will be delivered instead of APNs VoIP. </Note> ## Receive push notifications To receive push notifications, you should register the `BroadcastReceiver`. <Tabs> <Tab title="Java"> ```Java theme={null} BroadcastReceiver pushBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String message = intent.getStringExtra("message"); String from = intent.getStringExtra("from"); } }; LocalBroadcastManager.getInstance(this).registerReceiver(pushBroadcastReceiver, new IntentFilter("new-push-event")); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val pushBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val message = intent.getStringExtra("message") val from = intent.getStringExtra("from") } } LocalBroadcastManager.getInstance(this).registerReceiver(pushBroadcastReceiver, IntentFilter("new-push-event")) ``` </Tab> </Tabs> Or use the `onMessageReceived()` in your class that extends `QBFcmPushListenerService`. <Tabs> <Tab title="Java"> ```Java theme={null} @Override public void onMessageReceived(RemoteMessage remoteMessage) { String from = remoteMessage.getFrom(); Map<String, String> data = remoteMessage.getData(); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} override fun onMessageReceived(remoteMessage: RemoteMessage?) { val from = remoteMessage?.from val data = remoteMessage?.data } ``` </Tab> </Tabs> ## Troubleshooting #### Push notifications are not received on Android devices **Cause 1**: incorrect FCM server key is set in the Dashboard. **Tip**: check if you put a correct FCM server key into the **FCM API key** field in the **Dashboard => *YOUR\_APP* => Push Notifications => Settings**. See how to obtain the FCM server key and where to set it [here](/sdks/android-push-notifications#configure-firebase-project-and-api-key). It is possible that you are using the FCM sender ID instead of the FCM server key in the Dashboard. Try to send the message from a Firebase console directly. If a message has appeared, a problem is in the FCM server key. If the FCM server key is incorrect, then there will be the following error: `error 401 (Unauthorized, check your App auth_key)`. This means that the QuickBlox server wasn't able to authorize on the Firebase server. **Cause 2**: incorrect FCM sender ID in the `google-services.json` file. <Tip> **Tip**: check if you added a correct `google-services.json` file to your project. </Tip> **Cause 3**: the push is sent to the production zone while the device is subscribed to the development zone or vice versa. <Tip> **Tip**: check if you put the same FCM server key into the **FCM API key** field in the **Dashboard => *YOUR\_APP* => Push Notifications => Settings** for development and production to reduce the risk of error. </Tip> #### A subscription is removed after a push is sent and the push isn't delivered **Cause**: a device registration token is invalid. <Note> The device registration token is represented as `token` within the system. See [this section](/sdks/android-push-notifications#manual-push-subscription) to learn how to subscribe a device to push notifications. </Note> <Tip> **Tip**: check if the device registration is correct. The device registration token can be invalid due to a number of reasons: </Tip> 1. Some other data is set instead of a correct device registration token. For example, a Firebase project ID, Firebase user token, etc. 2. The client app unregistered itself from GCM/FCM. This can happen if the user uninstalls the application or, on iOS, if the APNs Feedback Service reported the APNs token as invalid. 3. The registration token expired. For example, Google might decide to refresh registration tokens or the APNs token may have expired for iOS devices. 4. The client app was updated, but the new version is not configured to receive messages. For all these cases, remove the invalid device registration token and stop using it to send messages. Then, obtain a new token and make sure to create a new subscription with a valid token. # Quick Start Source: https://docs.quickblox.com/sdks/android-quick-start Learn how to install QuickBlox SDK and send your first message. QuickBlox SDK helps you implement a real-time chat, video chat, and push notifications to your app. You can fully concentrate on your mobile app development. QuickBlox Android SDK supports both **Java** and **Kotlin** programming languages. ## Start with sample apps If you are just starting your app and developing it from scratch, we recommend to use our sample apps. We use GitHub repositories to make it easy to explore, copy, and modify our code samples. The guide on how to launch and configure the sample app is on GitHub. ### Chat samples Choose the code sample below to jump-start the development. <CardGroup> <Card title="Java Chat Sample App" icon="java"> <a href="https://github.com/QuickBlox/quickblox-android-sdk/tree/master/sample-chat-java"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/android-chat"> <Icon icon="book" /> Documentation </a> </Card> <Card title="Kotlin Chat Sample App" icon={<svg width="22px" height="23px" viewBox="0 0 22 23" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" > <defs> <linearGradient x1="0%" y1="0%" x2="50%" y2="50%" id="linearGradient-1"> <stop stopColor="#3978fc" offset="0%" /> <stop stopColor="#3978fc" offset="100%" /> </linearGradient> <linearGradient x1="-21.6846517%" y1="107.628997%" x2="84.7001079%" y2="0%" id="linearGradient-2"> <stop stopColor="#3978fc" offset="0%" /> <stop stopColor="#3978fc" offset="100%" /> </linearGradient> <linearGradient x1="0%" y1="72.8348211%" x2="100%" y2="-15.9724705%" id="linearGradient-3"> <stop stopColor="#3978fc" offset="0%" /> <stop stopColor="#3978fc" offset="100%" /> </linearGradient> </defs> <g fill="none" fillRule="evenodd"> <g transform="translate(-533.000000, -1249.000000)"> <g transform="translate(400.000000, 1109.000000)"> <g transform="translate(25.000000, 139.000000)"> <g transform="translate(108.000000, 1.500000)"> <polygon fill="url(https://mintlify.s3.us-west-1.amazonaws.com/quickblox/sdks#linearGradient-1)" transform="translate(10.875000, 10.875000) scale(1, -1) translate(-10.875000, -10.875000)" points="0 0 21.75 0 0 21.75" /> <polygon fill="url(https://mintlify.s3.us-west-1.amazonaws.com/quickblox/sdks#linearGradient-2)" points="0 0 21.75 0 0 21.75" /> <polygon fill="url(https://mintlify.s3.us-west-1.amazonaws.com/quickblox/sdks#linearGradient-3)" points="0 0 12 0 0 12" /> </g> </g> </g> </g> </g> </svg>} > <a href="https://github.com/QuickBlox/quickblox-android-sdk/tree/master/sample-chat-kotlin"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/android-chat"> <Icon icon="book" /> Documentation </a> </Card> </CardGroup> ### Video chat sample Choose the code sample below to jump-start the development. <CardGroup> <Card title="Java Video Calling Sample App" icon="java"> <a href="https://github.com/QuickBlox/quickblox-android-sdk/tree/master/sample-videochat-java"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/android-video-calling"> <Icon icon="book" /> Documentation </a> </Card> <Card title="Kotlin Video Calling Sample App" icon={<svg width="22px" height="23px" viewBox="0 0 22 23" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" > <defs> <linearGradient x1="0%" y1="0%" x2="50%" y2="50%" id="linearGradient-1"> <stop stopColor="#3978fc" offset="0%" /> <stop stopColor="#3978fc" offset="100%" /> </linearGradient> <linearGradient x1="-21.6846517%" y1="107.628997%" x2="84.7001079%" y2="0%" id="linearGradient-2"> <stop stopColor="#3978fc" offset="0%" /> <stop stopColor="#3978fc" offset="100%" /> </linearGradient> <linearGradient x1="0%" y1="72.8348211%" x2="100%" y2="-15.9724705%" id="linearGradient-3"> <stop stopColor="#3978fc" offset="0%" /> <stop stopColor="#3978fc" offset="100%" /> </linearGradient> </defs> <g fill="none" fillRule="evenodd"> <g transform="translate(-533.000000, -1249.000000)"> <g transform="translate(400.000000, 1109.000000)"> <g transform="translate(25.000000, 139.000000)"> <g transform="translate(108.000000, 1.500000)"> <polygon fill="url(https://mintlify.s3.us-west-1.amazonaws.com/quickblox/sdks#linearGradient-1)" transform="translate(10.875000, 10.875000) scale(1, -1) translate(-10.875000, -10.875000)" points="0 0 21.75 0 0 21.75" /> <polygon fill="url(https://mintlify.s3.us-west-1.amazonaws.com/quickblox/sdks#linearGradient-2)" points="0 0 21.75 0 0 21.75" /> <polygon fill="url(https://mintlify.s3.us-west-1.amazonaws.com/quickblox/sdks#linearGradient-3)" points="0 0 12 0 0 12" /> </g> </g> </g> </g> </g> </svg>} > <a href="https://github.com/QuickBlox/quickblox-android-sdk/tree/master/sample-videochat-kotlin"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/android-video-calling"> <Icon icon="book" /> Documentation </a> </Card> </CardGroup> ### More samples For more samples, head to our [Code Samples](/code-samples/code-samples) page. These sample apps are available on GitHub so feel free to browse them there. Just clone the repository and modify the source code for your own projects. ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app clicking **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click **Add** button. 4. Go to **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** . ## Requirements The minimum requirements for QuickBlox Android SDK are: * Android 5.0 (API 21) * Android Studio * Gradle 4.6 * Gradle plugin 2.5.1 ## Install QuickBlox SDK into your app To connect QuickBlox SDK to your app, import QuickBlox SDK dependencies via **build.gradle** file. Include reference to SDK repository in your **project-level build.gradle** file at the **root** directory. Specify the URL of QuickBlox repository where the files are stored. Following this URL gradle finds SDK artifacts. ```Groovy Groovy theme={null} allprojects { repositories { google() mavenCentral() maven { url "https://github.com/QuickBlox/quickblox-android-sdk-releases/raw/master/" } } } ``` When the artifacts are found in QuickBlox repository, they get imported to particular SDK modules in **build.gradle** project file. Add the following code lines to **app-level build.gradle** file. ```Groovy Groovy theme={null} dependencies { implementation "com.quickblox:quickblox-android-sdk-messages:4.3.0" implementation "com.quickblox:quickblox-android-sdk-chat:4.3.0" implementation "com.quickblox:quickblox-android-sdk-content:4.3.0" implementation "com.quickblox:quickblox-android-sdk-videochat-webrtc:4.3.0" implementation "com.quickblox:quickblox-android-sdk-conference:4.3.0" implementation "com.quickblox:quickblox-android-sdk-customobjects:4.3.0" } ``` ## Send your first message ### Initialize QuickBlox SDK Initialize the SDK with your application credentials. Pass the `APPLICATION_ID`, `AUTH_KEY`, `AUTH_SECRET`, and `ACCOUNT_KEY` to the `init()` method. <Tabs> <Tab title="Java"> ```Java theme={null} static final String APPLICATION_ID = "67895"; static final String AUTH_KEY = "lkjdueksu7392kj"; static final String AUTH_SECRET = "BTFsj7Rtt27DAmT"; static final String ACCOUNT_KEY = "9yvTe17TmjNPqDoYtfqp"; // QBSettings.getInstance().init(getApplicationContext(), APPLICATION_ID, AUTH_KEY, AUTH_SECRET); QBSettings.getInstance().setAccountKey(ACCOUNT_KEY); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} private const val APPLICATION_ID = "67895" private const val AUTH_KEY = "lkjdueksu7392kj" private const val AUTH_SECRET = "BTFsj7Rtt27DAmT" private const val ACCOUNT_KEY = "9yvTe17TmjNPqDoYtfqp" // QBSettings.getInstance().init(applicationContext, APPLICATION_ID, AUTH_KEY, AUTH_SECRET) QBSettings.getInstance().accountKey = ACCOUNT_KEY ``` </Tab> </Tabs> <Warning> You must initialize SDK before calling any methods through the SDK, except for the `init()` method. If you attempt to call a method without connecting, the error is returned. </Warning> <Tip> Security It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend and initialize QuickBlox SDK with applicationId and acountKey only. More details you can find in [Initialize QuickBlox SDK without Authorization Key and Secret](/sdks/android-setup#initialize-quickblox-sdk-without-authorization-key-and-secret) section. </Tip> ### Authorize user Now, it is time to log in with the user. To get it done, set the login and password of the `user`, call the `signIn()` method, and pass the `user` to it using the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} final QBUser user = new QBUser(); user.setLogin("johnsmith"); user.setPassword("johnPassword"); QBUsers.signIn(user).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin Kotlin theme={null} val user = QBUser() user.login = "johnsmith" user.password = "johnPassword" QBUsers.signIn(user).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ### Connect to chat Having authorized a user, you can proceed with connecting to the chat server to start using Chat module functionality. Call the `login()` method to connect to the chat server. <Tabs> <Tab title="Java"> ```Java Java theme={null} final QBUser user = new QBUser(); user.setId(12345); user.setPassword("johnPassword"); QBChatService.getInstance().login(user, new QBEntityCallback() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin Kotlin theme={null} val user = QBUser() user.id = 12315 user.password = "johnPassword" QBChatService.getInstance().login(user, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ### Create dialog QuickBlox provides three types of dialogs: **1-1 dialog**, **group dialog**, and **public dialog**. Learn more about dialogs [here](/sdks/android-chat-dialogs#create-dialog). Let’s create a simple **1-1 dialog**. Set the type and occupants IDs of the `dialog` using the the `setType()` and `setOccupantsIds()` methods. Then, call the `createChatDialog()` method and pass the `dialog` to it. <Tabs> <Tab title="Java"> ```Java Java theme={null} ArrayList<Integer> occupantIdsList = new ArrayList<Integer>(); int occupantId = 123; occupantIdsList.add(occupantId); QBChatDialog dialog = new QBChatDialog(); dialog.setType(QBDialogType.PRIVATE); dialog.setOccupantsIds(occupantIdsList); // or just use DialogUtils //QBChatDialog dialog = DialogUtils.buildPrivateDialog(recipientId); QBRestChatService.createChatDialog(dialog).performAsync(new QBEntityCallback<QBChatDialog>() { @Override public void onSuccess(QBChatDialog result, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin Kotlin theme={null} val occupantIdsList = ArrayList<Int>() val occupantId = 123 occupantIdsList.add(occupantId) val dialog = QBChatDialog() dialog.type = QBDialogType.PRIVATE dialog.setOccupantsIds(occupantIdsList) // or just use DialogUtils //QBChatDialog dialog = DialogUtils.buildPrivateDialog(recipientId); QBRestChatService.createChatDialog(dialog).performAsync(object : QBEntityCallback<QBChatDialog> { override fun onSuccess(result: QBChatDialog?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ### Subscribe to receive messages Through `QBDialogMessageListener` you can monitor whether an incoming message or error is received from QuickBlox server. Use `QBIncomingMessagesManager` to listen to all incoming messages and related errors. <Tabs> <Tab title="Java"> ```Java Java theme={null} QBChatService chatService = QBChatService.getInstance(); QBIncomingMessagesManager incomingMessagesManager = chatService.getIncomingMessagesManager(); incomingMessagesManager.addDialogMessageListener(new QBChatDialogMessageListener() { @Override public void processMessage(String dialogId, QBChatMessage chatMessage, Integer senderId) { } @Override public void processError(String dialogId, QBChatException exception, QBChatMessage chatMessage, Integer senderId) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin Kotlin theme={null} val chatService = QBChatService.getInstance() val incomingMessagesManager = chatService.incomingMessagesManager incomingMessagesManager.addDialogMessageListener(object : QBChatDialogMessageListener { override fun processMessage(dialogID: String?, qbChatMessage: QBChatMessage?, senderID: Int?) { } override fun processError(dialogID: String?, e: QBChatException?, qbChatMessage: QBChatMessage?, senderID: Int?) { } }) ``` </Tab> </Tabs> ### Send message To send a message, create `QBChatMessage` instance and set a text message using the `setBody()` method. Then, call the `sendMessage()` method. <Tabs> <Tab title="Java"> ```Java Java theme={null} QBChatMessage chatMessage = new QBChatMessage(); chatMessage.setBody("Hello QuickBlox!"); chatMessage.setSaveToHistory(true); dialog.sendMessage(chatMessage, new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin Kotlin theme={null} val chatMessage = QBChatMessage() chatMessage.body = "Hello QuickBlox!" chatMessage.setSaveToHistory(true) dialog.sendMessage(chatMessage, object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> <Note> Set the `saveToHistory` parameter if you want this message to be saved in the chat history. </Note> # AI Features Source: https://docs.quickblox.com/sdks/android-sdk-ai-features AI Features base on AI Agents ## Overview Starting from version 4.2.0 of QuickBlox Android SDK, the AI functionality is enabled and based on AI Agents. The QuickBlox Android SDK provides a range of features to enhance the chat experience. With essential messaging functionalities such as answer assistant, users can engage in more interactive conversations. **Supported features** | Name | Description | | :---------------- | :------------------------------------------------------------------------- | | AI Answers Assist | Generates a draft response based on chat history for the selected message. | | AI Translate | Provides translation based on chat history to selected incoming message. | ## Requirements The minimum requirements for using AI features are: * QuickBlox Android SDK v4.2.0 * QuickBlox account with activated AI Agents Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. Visit [AI Agent overview](/ai-agent/overview) page to learn the most important AI Agents concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](https://docs.quickblox.com/docs/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](https://docs.quickblox.com/docs/android-authentication) page to learn how to do it. 4. Create or update your AI Agent. See [AI Agent](/ai-agent/manage-agents#create-ai-agent) documentation to learn how to do it. ### Enable AI Extensions 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Extensions** page 2. Select the checkboxes for the features you want to enable. 3. Click the Save button to save changes. <img alt="Enable AI Extensions" /> ### Review your AI Agent To get **ID** of AI Agent to use it as `smartChatAssistantId` param follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Agent** page 2. Choose the ID of the AI Agent you want to update and click on it. 3. Edit the AI Agent settings. 4. Click the Save button to save changes. <img alt="Review AI Agent" /> ## AI Answer Assist QuickBlox AI Answer Assist generates a contextually relevant draft response based on the chat history. Use it to suggest replies that match the conversation context. ### How to use Answer Assist <Tabs> <Tab title="Java"> ```java theme={null} String smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX"; String messageToAssist = "What is Quickblox?"; List<QBAIAnswerAssistHistoryMessage> history = new ArrayList<>(); history.add(new QBAIAnswerAssistHistoryMessageImpl(Role.USER, "Hi")); history.add(new QBAIAnswerAssistHistoryMessageImpl(Role.ASSISTANT, "Hello! How can I assist you today?")); QB.ai.answerAssist(smartChatAssistantId, messageToAssist, history).performAsync(new QBEntityCallback<QBAIAnswerAssistResult>() { @Override public void onSuccess(QBAIAnswerAssistResult qbaiAnswerAssistResult, Bundle bundle) { String answer = qbaiAnswerAssistResult.getMessage(); // handle answer } @Override public void onError(QBResponseException exception) { // handle error } }); ``` </Tab> <Tab title="Kotlin"> ```kotlin theme={null} val smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" val messageToAssist = "What is Quickblox?" val history: MutableList<QBAIAnswerAssistHistoryMessage> = ArrayList() history.add(QBAIAnswerAssistHistoryMessageImpl(Role.USER, "Hi")) history.add(QBAIAnswerAssistHistoryMessageImpl(Role.ASSISTANT, "Hello! How can I assist you today?")) QB.ai.answerAssist(smartChatAssistantId, messageToAssist, history) .performAsync(object : QBEntityCallback<QBAIAnswerAssistResult?> { override fun onSuccess(qbaiAnswerAssistResult: QBAIAnswerAssistResult?, bundle: Bundle) { val answer = qbaiAnswerAssistResult?.message // handle answer } override fun onError(exception: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> | Parameter name | Type | Description | | :------------------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | smartChatAssistantId | String | This field should hold your actual Smart Chat Assistant ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | | messageToAssist | String | The message you want to get an answer for. | | history | Array of Object | Conversation history. Used to add context. Each object of the array should have two fields: 'role' and 'message'. The field role should contain one of the next values: `Role.USER` or `Role.ASSISTANT`. The field message should be a String with a chat message. | ## AI Translate QuickBlox offers translation functionality that helps users easily translate text messages in chat, taking into account the context of the chat history. ### How to use AI Translate <Tabs> <Tab title="Java"> ```java theme={null} String smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX"; String textToTranslate = "Hola!"; String languageCode = "en"; QB.ai.translate(smartChatAssistantId, textToTranslate, languageCode).performAsync(new QBEntityCallback<QBAITranslateResult>() { @Override public void onSuccess(QBAITranslateResult qbaiTranslateResult, Bundle bundle) { String translation = qbaiTranslateResult.getMessage(); // handle translation } @Override public void onError(QBResponseException e) { // handle error } }); ``` </Tab> <Tab title="Java"> ```kotlin theme={null} val smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" val textToTranslate = "Hola!" val languageCode = "en" QB.ai.translate(smartChatAssistantId, textToTranslate, languageCode) .performAsync(object : QBEntityCallback<QBAITranslateResult> { override fun onSuccess(qbaiTranslateResult: QBAITranslateResult, bundle: Bundle) { val translation = qbaiTranslateResult.message // handle translation } override fun onError(e: QBResponseException) { // handle error } }) ``` </Tab> </Tabs> | Parameter name | Type | Description | | :------------------- | :----- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual Smart Chat Assistant ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | | textToTranslate | String | Text to translate. | | languageCode | String | Translation language code. | # SDK Reference Source: https://docs.quickblox.com/sdks/android-sdkreference # Setup Source: https://docs.quickblox.com/sdks/android-setup Learn how to add and configure QuickBlox SDK for your app. Follow the instructions below to ensure that QuickBlox Android SDK runs smoothly with your app. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app by clicking the **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click the **Add** button. 4. Go to the **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** . ## Requirements The minimum requirements for QuickBlox Android SDK are: * Android 5.0 (API 21) * Android Studio * Gradle 4.6 * Gradle plugin 2.5.1 ## Install QuickBlox SDK into your app To connect QuickBlox SDK to your app, import QuickBlox SDK dependencies via **build.gradle** file. Include reference to SDK repository in your **project-level build.gradle** file at the **root** directory. Specify the URL of QuickBlox repository where the files are stored. Following this URL, gradle finds SDK artifacts. ```Groovy Groovy theme={null} allprojects { repositories { google() mavenCentral() maven { url "https://github.com/QuickBlox/quickblox-android-sdk-releases/raw/master/" } } } ``` When the artifacts are found in QuickBlox repository, they get imported to particular SDK modules in **build.gradle** project file. Add the following code lines to **app-level build.gradle** file. ```Groovy Groovy theme={null} dependencies { implementation "com.quickblox:quickblox-android-sdk-messages:4.3.0" implementation "com.quickblox:quickblox-android-sdk-chat:4.3.0" implementation "com.quickblox:quickblox-android-sdk-content:4.3.0" implementation "com.quickblox:quickblox-android-sdk-videochat-webrtc:4.3.0" implementation "com.quickblox:quickblox-android-sdk-conference:4.3.0" implementation "com.quickblox:quickblox-android-sdk-customobjects:4.3.0" } ``` | Parameters | Description | | ------------- | ---------------------------------------------------------------------------------------------- | | messages | Push Notifications module enables working with push notifications and alerts to users. | | chat | Chat module allows creating dialogs and sending messages into these dialogs. | | content | Content module enables file storage and creating chat attachments for your app. | | webrtc | Video Calling module adds video and audio calling features to your app. | | conference | Video Conference allows setting up a video conference between 10-12 people in your app. | | customobjects | Custom Objects module provides flexibility to define any data structure you need for your app. | ## Add permissions To use the QuickBlox SDK, you need to add the following permissions to your app manifest: ```XML XML theme={null} <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> ``` To use video calling in your app, make sure to add the following minimum required permissions to your app manifest: ```XML XML theme={null} <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.CAMERA"/> ``` To use chat functionality in your app and be able to send/receive files in the messages, make sure to add the following minimum required permissions to your app manifest: ```XML XML theme={null} <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> ``` If you need to add any other functionality to your app, you can add the relevant permissions to your app manifest. See [this section](https://developer.android.com/training/permissions/declaring) to learn how to declare permissions. See [this section](https://developer.android.com/training/permissions/requesting) to learn how to request permissions. <Note> Note that mentioning the camera and microphone permissions in the manifest isn't always enough. You need to request camera and microphone permissions additionally at runtime. </Note> ## Initialize QuickBlox SDK Initialize the framework with your application credentials. Pass **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** to the `init()` method. <Warning> You must initialize SDK before calling any methods through the SDK, except for the `init()` method. If you attempt to call a method without connecting, the error is returned. </Warning> <Tabs> <Tab title="Java"> ```Java theme={null} static final String APPLICATION_ID = "67895"; static final String AUTH_KEY = "lkjdueksu7392kj"; static final String AUTH_SECRET = "BTFsj7Rtt27DAmT"; static final String ACCOUNT_KEY = "9yvTe17TmjNPqDoYtfqp"; QBSettings.getInstance().init(getApplicationContext(), APPLICATION_ID, AUTH_KEY, AUTH_SECRET); QBSettings.getInstance().setAccountKey(ACCOUNT_KEY); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} private const val APPLICATION_ID = "67895" private const val AUTH_KEY = "lkjdueksu7392kj" private const val AUTH_SECRET = "BTFsj7Rtt27DAmT" private const val ACCOUNT_KEY = "9yvTe17TmjNPqDoYtfqp" QBSettings.getInstance().init(applicationContext, APPLICATION_ID, AUTH_KEY, AUTH_SECRET) QBSettings.getInstance().accountKey = ACCOUNT_KEY ``` </Tab> </Tabs> <Warning> Security It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend. </Warning> | Argument | Required | Description | | --------------- | -------- | -------------------------------------------------------------------------------- | | APPLICATION\_ID | yes | Application identifier. | | AUTH\_KEY | yes | Authorization key. | | AUTH\_SECRET | yes | Authorization secret. | | ACCOUNT\_KEY | yes | Account key. Required to get actual Chat and API endpoints for the right server. | ## Initialize QuickBlox SDK without Authorization Key and Secret You may don't want to store **authKey** and **authSecret** inside an application for security reasons. In such case, you can initialize QuickBlox SDK with **applicationId** and **accountKey** only, and store your **authKey** and **authSecret** on your backend. But, if so, the implementation of [authentication with QuickBlox](https://docs.quickblox.com/reference/authentication) should be also moved to your backend. <Tabs> <Tab title="Java"> ```Java theme={null} static final String APPLICATION_ID = "67895"; static final String ACCOUNT_KEY = "9yvTe17TmjNPqDoYtfqp"; QBSDK.initWithAppId(getApplicationContext(), APPLICATION_ID, ACCOUNT_KEY); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} private const val APPLICATION_ID = "67895" private const val ACCOUNT_KEY = "9yvTe17TmjNPqDoYtfqp" QBSDK.initWithAppId(applicationContext, APPLICATION_ID, ACCOUNT_KEY) ``` </Tab> </Tabs> Then using your backend you can authorize a user in the QuickBlox system, send back the user session token, and set it to the QuickBlox SDK using `QBAuth.startSessionWithToken()` method. You can find out more about this in the \[Set existing session]\(/sdks/android-authentication #set-existing-session) section. ## Point SDK to the enterprise server To point QuickBlox SDK to the QuickBlox enterprise server, you should pass the `API_ENDPOINT` and `CHAT_ENDPOINT` to the `setEndpoints()` method. You can call this method only after [initializing the SDK](/sdks/ios-setup#initialize-quickblox-sdk). <Tabs> <Tab title="Java"> ```Java theme={null} private static final String API_ENDPOINT = "https://apicustomdomain.quickblox.com"; private static final String CHAT_ENDPOINT = "chatcustomdomain.quickblox.com"; // use this method only if you need to set custom endpoints QBSettings.getInstance().setEndpoints(API_ENDPOINT, CHAT_ENDPOINT), ServiceZone.PRODUCTION); QBSettings.getInstance().setZone(ServiceZone.PRODUCTION); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} private const val API_ENDPOINT = "https://apicustomdomain.quickblox.com" private const val CHAT_ENDPOINT = "chatcustomdomain.quickblox.com" // use the this method only if you need to set custom endpoints QBSettings.getInstance().setEndpoints(API_ENDPOINT, CHAT_ENDPOINT, ServiceZone.PRODUCTION) QBSettings.getInstance().zone = ServiceZone.PRODUCTION ``` </Tab> </Tabs> | Argument | Required | Description | | -------------- | -------- | --------------------------------------------------------------------------------- | | API\_ENDPOINT | yes | API endpoint. | | CHAT\_ENDPOINT | yes | Chat endpoint. | | serviceZone | yes | Connection service zone. The area where push notifications and messages can work. | <Note> [Contact our sales team](https://quickblox.com/enterprise/) to get **API endpoint** and **chat endpoint**. </Note> ## Enable logging Logging functionality allows you to keep track of all events and activities while running your app. As a result, you can monitor the operation of the SDK and improve the debug efficiency. There are 3 logging use cases: * **Server API** logging is used to monitor Server API calls. * **Chat** logging is used to monitor chat issues. * **WebRTC** logging is used to gather issues with video. **Server API logging** Enable Server API calls debug console output using the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBSettings.getInstance().setLogLevel(LogLevel.DEBUG); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBSettings.getInstance().logLevel = LogLevel.DEBUG ``` </Tab> </Tabs> | Parameters | Description | | ---------------- | -------------------------------- | | LogLevel.NOTHING | Write nothing. Turn off logs. | | LogLevel.DEBUG | Enable logs (**default** value). | **Chat logging** Use the method below to enable a detailed XMPP logging in the console output. `true` is enabled, `false` is disabled. Default: `false`. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatService.setDebugEnabled(true); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBChatService.setDebugEnabled(true) ``` </Tab> </Tabs> **WebRTC logging** To enable WebRTC logging, use the `setDebugEnabled()` method. `true` is enabled, `false` is disabled. Default: **true**. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCConfig.setDebugEnabled(true); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBRTCConfig.setDebugEnabled(true) ``` </Tab> </Tabs> ## Enable auto-reconnect to Chat QuickBlox Chat runs over XMPP protocol. To receive messages in a real-time mode, the application should be connected to the Chat over XMPP protocol. To enable auto-reconnect to Chat, call the `setReconnectionAllowed()` method and pass `true`. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatService.getInstance().setReconnectionAllowed(true) ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBChatService.getInstance().isReconnectionAllowed = true ``` </Tab> </Tabs> <Note> **By default**, autoreconnection functionality is enabled. Set autoreconnection before calling the `QBChatService.getInstance().login()` method so it could be applied in a current chat. </Note> ## Message carbons Message carbons functionality allows for multi-device support. Thus, all user messages get copied to all their devices so they could keep up with the current state of the conversation. For example, a User A has phone running conversations and desktop running conversations. User B has desktop running conversations. When User B sends a message to User A, the message shows on both the desktop and phone of User A. **Enable message carbons** <Tabs> <Tab title="Java"> ```Java theme={null} try { QBChatService.getInstance().enableCarbons(); } catch (XMPPException exception) { exception.printStackTrace(); } catch (SmackException exception) { exception.printStackTrace(); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { QBChatService.getInstance().enableCarbons() } catch (exception: XMPPException) { exception.printStackTrace() } catch (exception: SmackException) { exception.printStackTrace() } ``` </Tab> </Tabs> **Disable message carbons** <Tabs> <Tab title="Java"> ```Java theme={null} try { QBChatService.getInstance().disableCarbons(); } catch (XMPPException exception) { exception.printStackTrace(); } catch (SmackException exception) { exception.printStackTrace(); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { QBChatService.getInstance().disableCarbons() } catch (exception: XMPPException) { exception.printStackTrace() } catch (exception: SmackException) { exception.printStackTrace() } ``` </Tab> </Tabs> <Note> **By default**, message carbons functionality is disabled. </Note> <Tip> Since message carbons functionality works over XMPP connection, make sure to enable it after the `QBChatService.getInstance().login()` method is called. </Tip> ## Stream management Stream management has two important features **Stanza Acknowledgements** and **Stream Resumption**: * **Stanza Acknowledgements** is the ability to know if a stanza or series of stanzas has been received by one's peer. In other words, a reply is requested on every sent message. If the reply is received, the message is considered as delivered. * **Stream Resumption** is the ability to quickly resume a stream that has been terminated. Once a connection is re-established, Stream Resumption is executed. By matching the sequence numbers assigned to each Stanza Acknowledgement a server and client can verify which messages are missing and request to resend missing messages. <Tabs> <Tab title="Java"> ```Java theme={null} QBChatService.getInstance().setUseStreamManagement(true); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBChatService.getInstance().setUseStreamManagement(true) ``` </Tab> </Tabs> <Note> **By default**, stream management functionality is disabled. </Note> <Tip> You should enable Stream Management before you do the `login()` because the Stream Management is initialized while Chat login is performed. The Stream Management defines an extension for active management of a stream between a client and server, including features for stanza acknowledgments. </Tip> ## Configure media settings You can use a `QBRTCMediaConfig` class instance to configure a various list of media settings like video/audio codecs, bitrate, fps, etc. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCMediaConfig.setAudioCodec(QBRTCMediaConfig.AudioCodec.ISAC); QBRTCMediaConfig.setAudioCodec(QBRTCMediaConfig.AudioCodec.OPUS); QBRTCMediaConfig.setVideoCodec(QBRTCMediaConfig.VideoCodec.H264); QBRTCMediaConfig.setVideoCodec(QBRTCMediaConfig.VideoCodec.VP8); QBRTCMediaConfig.setVideoCodec(QBRTCMediaConfig.VideoCodec.VP9); QBRTCMediaConfig.setAudioStartBitrate(audioStartBitrate); QBRTCMediaConfig.setVideoStartBitrate(videoStartBitrate); QBRTCMediaConfig.setVideoWidth(videoWidth); QBRTCMediaConfig.setVideoHeight(videoHeight); // Enable Hardware Acceleration if device supports it QBRTCMediaConfig.setVideoHWAcceleration(true); // Set frames-per-second in transmitting video stream QBRTCMediaConfig.setVideoFps(videoFPS); // Enable built-in AEC if device supports it QBRTCMediaConfig.setUseBuildInAEC(true); // Enable OpenSL ES audio if device supports it QBRTCMediaConfig.setUseOpenSLES(true); QBRTCMediaConfig.setAudioProcessingEnabled(true); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBRTCMediaConfig.setAudioCodec(QBRTCMediaConfig.AudioCodec.ISAC) QBRTCMediaConfig.setAudioCodec(QBRTCMediaConfig.AudioCodec.OPUS) QBRTCMediaConfig.setVideoCodec(QBRTCMediaConfig.VideoCodec.H264) QBRTCMediaConfig.setVideoCodec(QBRTCMediaConfig.VideoCodec.VP8) QBRTCMediaConfig.setVideoCodec(QBRTCMediaConfig.VideoCodec.VP9) QBRTCMediaConfig.setAudioStartBitrate(audioStartBitrate) QBRTCMediaConfig.setVideoStartBitrate(videoStartBitrate) QBRTCMediaConfig.setVideoWidth(videoWidth) QBRTCMediaConfig.setVideoHeight(videoHeight) // Enable Hardware Acceleration if device supports it QBRTCMediaConfig.setVideoHWAcceleration(true) // Set frames-per-second in transmitting video stream QBRTCMediaConfig.setVideoFps(videoFPS) // Enable built-in AEC if device supports it QBRTCMediaConfig.setUseBuildInAEC(true) // Enable OpenSL ES audio if device supports it QBRTCMediaConfig.setUseOpenSLES(true) QBRTCMediaConfig.setAudioProcessingEnabled(true) ``` </Tab> </Tabs> <Note> Make sure to setup media settings before initiating a call. </Note> ## Synchronous and Asynchronous performers You can use different performers `perform()` or `performAsync()`. Please note that the `QBUsers.getUser()` method is used there as an example. You can use these performers in all cases where performers are available. In a synchronous way, you should handle `QBResponseException`. <Tabs> <Tab title="Java"> ```Java theme={null} try { QBUsers.getUser(user.getId()).perform(); } catch (QBResponseException exception) { // handling exception } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { QBUsers.getUser(user.id).perform() } catch (exception: QBResponseException) { // handling exception } ``` </Tab> </Tabs> In an asynchronous way, you can make further logic according to the server response. <Tabs> <Tab title="Java"> ```Java theme={null} QBUsers.getUser(user.getId()).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBUsers.getUser(user.id).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Custom ICE servers You can customize a list of ICE servers. **By default**, WebRTC module will use internal ICE servers that are usually enough, but you can always set your own. WebRTC engine will choose the TURN relay with the lowest round-trip time. Thus, setting multiple TURN servers allows your application to scale-up in terms of bandwidth and number of users. <Tabs> <Tab title="Java"> ```Java theme={null} // Set custom ICE servers up. Use it in case you want set YOUR OWN servers instead of defaults List<PeerConnection.IceServer> iceServerList = new LinkedList<>(); iceServerList.add(new PeerConnection.IceServer("stun:stun.randomserver.example","", "")); iceServerList.add(new PeerConnection.IceServer("stun:stun.randomserver.example","stun_login", "hdccn97ba2d56d72i426eb9875bya7yte8")); iceServerList.add(new PeerConnection.IceServer("turn:turn.randomserver.example:6789?transport=udp","turn_login", "78kkb67m2f45h1e27ub9886gt70109")); iceServerList.add(new PeerConnection.IceServer("turn:turn.randomserver.example:6789?transport=tcp","turn_login", "78kkb67m2f45h1e27ub9886gt70109")); QBRTCConfig.setIceServerList(iceServerList); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // Set custom ICE servers up. Use it in case you want set YOUR OWN servers instead of defaults val iceServerList = LinkedList<PeerConnection.IceServer>() iceServerList.add(PeerConnection.IceServer("stun:stun.randomserver.example", "", "")) iceServerList.add(PeerConnection.IceServer("stun:turn.randomserver.example", "stun_login", "hdccn97ba2d56d72i426eb9875bya7yte8")) iceServerList.add(PeerConnection.IceServer("turn:turn.randomserver.example:6789?transport=udp", "turn_login", "78kkb67m2f45h1e27ub9886gt70109")) iceServerList.add(PeerConnection.IceServer("turn:turn.randomserver.example:6789?transport=tcp", "turn_login", "78kkb67m2f45h1e27ub9886gt70109")) QBRTCConfig.setIceServerList(iceServerList) ``` </Tab> </Tabs> # Users Source: https://docs.quickblox.com/sdks/android-users Learn how to manage your users with QuickBlox. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. ## Create user It's recommended to manage user creation at your backend for production. To learn more you can refer to [QuickBlox API documentation](https://docs.quickblox.com/reference/create-user). For POCs/MVPs or during development you may want to create users on the fly, you can use `signUp()` method. Create a user using the code snippet below. Only login (or email) and password are required. Other fields are optional. <Tabs> <Tab title="Java"> ```Java theme={null} QBUser user = new QBUser(); user.setLogin("johnsmith"); user.setPassword("johnPassword"); QBUsers.signUp(user).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val user = QBUser() user.login = "johnsmith" user.password = "johnPassword" QBUsers.signUp(user).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException? { } }) ``` </Tab> </Tabs> <Warning> **Security & Privacy** It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. Email, full name, facebookId and phone number are PII, [configure session permissions](/docs/application#set-session-permissions) according to your privacy requirements. </Warning> ## Retrieve users Get a list of users using the `getUsers()` method. The code snippet below shows how to get a list of users created between the two given dates, and sorted in descending order. <Tabs> <Tab title="Java"> ```Java theme={null} String field = "created_at"; String typeField = "date"; String sortDesc = "desc"; String sortAsc = "asc"; String paramSort = "order"; String paramFilter = "filter[]"; String searchOperator = "between"; String searchValue = "2021-01-01, 2021-05-06"; ArrayList<GenericQueryRule> rules = new ArrayList<GenericQueryRule>(); rules.add(new GenericQueryRule(paramSort, sortDesc + " " + typeField + " " + field)); rules.add(new GenericQueryRule(paramFilter, typeField + " " + field + " " + searchOperator + " " + searchValue)); QBPagedRequestBuilder requestBuilder = new QBPagedRequestBuilder(); requestBuilder.setRules(rules); requestBuilder.setPerPage(100); requestBuilder.setPage(1); QBUsers.getUsers(requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBUser>>() { @Override public void onSuccess(ArrayList<QBUser> usersList, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val field = "created_at" val typeField = "date" val sortDesc = "desc" val sortAsc = "asc" val paramSort = "order" val paramFilter ="filter[]" val searchOperator = "between" val searchValue = "2021-01-01, 2021-05-06" val rules = ArrayList<GenericQueryRule>() rules.add(GenericQueryRule(paramSort, "$sortDesc $typeField $field")) rules.add(GenericQueryRule(paramFilter,"$typeField $field $searchOperator $searchValue")) val requestBuilder = QBPagedRequestBuilder() requestBuilder.rules = rules requestBuilder.perPage = 100 requestBuilder.page = 1 QBUsers.getUsers(requestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBUser>> { override fun onSuccess(usersList: ArrayList<QBUser>, bundle: Bundle) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> | Argument | Required | Description | | -------------- | -------- | ----------------------------------------- | | requestBuilder | yes | Allows to set parameters for the request. | If you want to retrieve only users updated after some specific date time and order search results, you can use operators. This is useful if you cache users somehow and do not want to obtain the whole list of users on every app start. Thus, you can use [search](/sdks/android-users#search-operators) and [sort](/sdks/android-users#sort-operators) operators to list users on the page so that it is easier to see specific users. If you want to get a paginated list of users from the server, you can set the following fields in the `QBPagedRequestBuilder` class: | Field | Required | Description | | ------- | -------- | -------------------------------------------- | | perPage | no | Number of records to return in one page. | | page | no | Number of pages with results to be returned. | ### Search operators You can use search operators to get more specific search results. The code snippet below shows how to get a list of users filtered by the `created_at` field and created between the two given dates. <Tabs> <Tab title="Java"> ```Java theme={null} String field = "created_at"; String typeField = "date"; String paramFilter = "filter[]"; String searchOperator = "between"; String searchValue = "2021-01-01, 2021-05-06"; ArrayList<GenericQueryRule> rules = new ArrayList<GenericQueryRule>(); rules.add(new GenericQueryRule(paramFilter, typeField + " " + field + " " + searchOperator + " " + searchValue)); QBPagedRequestBuilder requestBuilder = new QBPagedRequestBuilder(); requestBuilder.setRules(rules); QBUsers.getUsers(requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBUser>>() { @Override public void onSuccess(ArrayList<QBUser> usersList, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val field = "created_at" val typeField = "date" val paramFilter ="filter[]" val searchOperator = "between" val searchValue = "2021-01-01, 2021-05-06" val rules = ArrayList<GenericQueryRule>() rules.add(GenericQueryRule(paramFilter,"$typeField $field $searchOperator $searchValue")) val requestBuilder = QBPagedRequestBuilder() requestBuilder.rules = rules QBUsers.getUsers(requestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBUser>> { override fun onSuccess(usersList: ArrayList<QBUser>, bundle: Bundle) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> Here are the search operators you can use to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ | | lt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less Than** operator. | | gt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** operator. | | gte | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** or **Equal** to operator. | | le | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less or Equal to** operator | | eq | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Equal** to operator. | | ne | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Not Equal** to operator. | | between | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Contained between values** operator. | | in | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **IN** array operator. | ### Sort operators You can use sort operators to order the search results. The request below will return users by the `created_at` field sorted in descending order. <Tabs> <Tab title="Java"> ```Java theme={null} String field = "created_at"; String typeField = "date"; String sortDesc = "desc"; String paramSort = "order"; ArrayList<GenericQueryRule> rules = new ArrayList<GenericQueryRule>(); rules.add(new GenericQueryRule(paramSort, sortDesc + " " + typeField + " " + field)); QBPagedRequestBuilder requestBuilder = new QBPagedRequestBuilder(); requestBuilder.setRules(rules); QBUsers.getUsers(requestBuilder).performAsync(new QBEntityCallback<ArrayList<QBUser>>() { @Override public void onSuccess(ArrayList<QBUser> usersList, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val field = "created_at" val typeField = "date" val sortDesc = "desc" val paramSort = "order" val rules = ArrayList<GenericQueryRule>() rules.add(GenericQueryRule(paramSort, "$sortDesc $typeField $field")) val requestBuilder = QBPagedRequestBuilder() requestBuilder.rules = rules QBUsers.getUsers(requestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBUser>> { override fun onSuccess(usersList: ArrayList<QBUser>, bundle: Bundle) { } override fun onError(exception: QBResponseException) { } }) ``` </Tab> </Tabs> Here are the sort operators you can use to order the search results. | Sort operator | Applicable to types | Applicable to fields | Description | | ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | | sortAsc | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Search results will be sorted in ascending order by the specified field. | | sortDesc | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Search results will be sorted in descending order by the specified field. | ## Retrieve users by ID To get a list of users by ID for a current account, use the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} QBPagedRequestBuilder pagedRequestBuilder = new QBPagedRequestBuilder(); pagedRequestBuilder.setPage(1); pagedRequestBuilder.setPerPage(50); ArrayList<Integer> usersIDs = new ArrayList<>(); usersIDs.add(123); usersIDs.add(234); usersIDs.add(345); usersIDs.add(456); QBUsers.getUsersByIDs(usersIds, pagedRequestBuilder).performAsync(new QBEntityCallback<ArrayList<QBUser>>() { @Override public void onSuccess(ArrayList<QBUser> users, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val pagedRequestBuilder = QBPagedRequestBuilder() pagedRequestBuilder.page = 1 pagedRequestBuilder.perPage = 50 val usersIDs = ArrayList<Int>() usersIDs.add(123) usersIDs.add(234) usersIDs.add(345) usersIDs.add(456) QBUsers.getUsersByIDs(usersIds, pagedRequestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBUser>> { override fun onSuccess(users: ArrayList<QBUser>?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> Or, you can retrieve a single user if needed. <Tabs> <Tab title="Java"> ```Java theme={null} QBUsers.getUser(user.getId()).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBUsers.getUser(user.id).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Retrieve user by login To get a list of users by login for a current account, use the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} String userLogin = user.getLogin(); QBUsers.getUserByLogin(userLogin).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val userLogin = user.login QBUsers.getUserByLogin(userLogin).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException? { } }) ``` </Tab> </Tabs> ## Retrieve user by email To get a list of users by email for a current account, use the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} String usersEmail = user.getEmail(); QBUsers.getUserByEmail(usersEmail).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val usersEmail = user.email QBUsers.getUserByEmail(usersEmail).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Retrieve users by full name To get a list of users found by the server according to the search query, use the following code snippet. Search **requires** min 3 characters. <Tabs> <Tab title="Java"> ```Java theme={null} QBPagedRequestBuilder pagedRequestBuilder = new QBPagedRequestBuilder(); pagedRequestBuilder.setPage(1); pagedRequestBuilder.setPerPage(50); QBUsers.getUsersByFullName(searchQuery, pagedRequestBuilder).performAsync(new QBEntityCallback<ArrayList<QBUser>>() { @Override public void onSuccess(ArrayList<QBUser> users, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val pagedRequestBuilder = QBPagedRequestBuilder() pagedRequestBuilder.page = 1 pagedRequestBuilder.perPage = 50 QBUsers.getUsersByFullName(searchQuery, pagedRequestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBUser>> { override fun onSuccess(users: ArrayList<QBUser>?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Retrieve users by phone number To get a list of users by phone number for a current account, use the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} QBPagedRequestBuilder pagedRequestBuilder = new QBPagedRequestBuilder(); pagedRequestBuilder.setPage(1); pagedRequestBuilder.setPerPage(50); ArrayList<String> phoneNumbers = new ArrayList<>(); phoneNumbers.add("+380552223344"); phoneNumbers.add("+190229996677"); phoneNumbers.add("78088800444"); QBUsers.getUsersByPhoneNumbers(phoneNumbers, pagedRequestBuilder).performAsync(new QBEntityCallback<ArrayList<QBUser>>() { @Override public void onSuccess(ArrayList<QBUser> users, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val pagedRequestBuilder = QBPagedRequestBuilder() pagedRequestBuilder.page = 1 pagedRequestBuilder.perPage = 50 val phoneNumbers = ArrayList<String>() phoneNumbers.add("+380552223344") phoneNumbers.add("+190229996677") phoneNumbers.add("78088800444") QBUsers.getUsersByPhoneNumbers(phoneNumbers, pagedRequestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBUser>> { override fun onSuccess(users: ArrayList<QBUser>?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Retrieve user by external user ID If you have your own database with users (we call these databases as "external databases"), you can use External User ID (`ExternalID` field) in `QBUser` model to link users from QuickBlox with users from your external database. <Tabs> <Tab title="Java"> ```Java theme={null} QBUsers.getUserByExternalId(user.getExternalId()).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBUsers.getUserByExternalId(user.externalId).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Retrieve users by Facebook ID To get a list of users by Facebook ID for a current account, use the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} QBUsers.getUserByFacebookId(user.getFacebookId()).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBUsers.getUserByFacebookId(user.facebookId).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> Or, if you want to retrieve several users by Facebook IDs, use the following code lines. <Tabs> <Tab title="Java"> ```Java theme={null} ArrayList<String> facebookIds = new ArrayList<>(); facebookIds.add("552223344"); facebookIds.add("90229996677"); facebookIds.add("78088800444"); QBPagedRequestBuilder pagedRequestBuilder = new QBPagedRequestBuilder(); pagedRequestBuilder.setPage(1); pagedRequestBuilder.setPerPage(50); QBUsers.getUsersByFacebookId(facebookIds, pagedRequestBuilder).performAsync(new QBEntityCallback<ArrayList<QBUser>>() { @Override public void onSuccess(ArrayList<QBUser> users, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val facebookIds = ArrayList<String>() facebookIds.add("552223344") facebookIds.add("90229996677") facebookIds.add("78088800444") val pagedRequestBuilder = QBPagedRequestBuilder() pagedRequestBuilder.page = 1 pagedRequestBuilder.perPage = 50 QBUsers.getUsersByFacebookId(facebookIds, pagedRequestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBUser>> { override fun onSuccess(users: ArrayList<QBUser>?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Retrieve users by tags To get a list of users by tags for a current account, use the following code snippet. <Tabs> <Tab title="Java"> ```Java theme={null} QBPagedRequestBuilder pagedRequestBuilder = new QBPagedRequestBuilder(); pagedRequestBuilder.setPage(1); pagedRequestBuilder.setPerPage(50); ArrayList<String> tags = new ArrayList<>(); tags.add("doctor"); tags.add("english"); tags.add("francias"); QBUsers.getUsersByTags(tags, pagedRequestBuilder).performAsync(new QBEntityCallback<ArrayList<QBUser>>() { @Override public void onSuccess(ArrayList<QBUser> users, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val pagedRequestBuilder = QBPagedRequestBuilder() pagedRequestBuilder.page = 1 pagedRequestBuilder.perPage = 50 val tags = ArrayList<String>() tags.add("doctor") tags.add("english") tags.add("francias") QBUsers.getUsersByTags(tags, pagedRequestBuilder).performAsync(object : QBEntityCallback<ArrayList<QBUser>> { override fun onSuccess(users: ArrayList<QBUser>?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Delete user A user can delete himself from the platform. <Tabs> <Tab title="Java"> ```Java theme={null} QBUsers.deleteUser(user.getId()).performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBUsers.deleteUser(user.id).performAsync(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> ## Reset user password It's possible to reset a password via email. <Tabs> <Tab title="Java"> ```Java theme={null} QBUsers.resetPassword(user.getEmail()).performAsync(new QBEntityCallback<Void>() { @Override public void onSuccess(Void aVoid, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBUsers.resetPassword(user.email).performAsync(object : QBEntityCallback<Void> { override fun onSuccess(aVoid: Void?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> <Note> Make sure to enable the email confirmation. This functionality allows application users to confirm their emails. If a user doesn't confirm the email, the emails won't be sent to this user. As a result, a password reset functionality won't work. To enable the email confirmation, proceed as follows: 1. Go to the **Dashboard => *YOUR\_APP* => Users => Settings => User registration confirmation** and check the box. 2. Click the **Save** button. </Note> <Warning> A password reset functionality is available for the Enterprise plan. [Contact the sales team](https://quickblox.com/enterprise/#get) for more details. </Warning> ## Update user Update a user profile by calling the `updateUser()` method. If you want to change your password, you need to provide 2 parameters: `password` and `newPassword`. The updated `user` entity will be returned. <Warning> You must set `null` into the user's `password` field if you do not want to change your password. Otherwise, you will need to add the user's old password to the `oldPassword` field. </Warning> You can update any other field of the `user` using the `updateUser()` method. Thus, the snippet below shows how to update a `tagList` and `customData` fields. <Tabs> <Tab title="Java"> ```Java theme={null} // set user's tag or tags StringifyArrayList<String> userTags = new StringifyArrayList<>(); userTags.add("tagOne"); userTags.add("tagTwo"); user.setTags(userTags); // set user's custom data JSONObject jsonObject = new JSONObject(); try { jsonObject.put("name", "John"); jsonObject.put("age", 31); jsonObject.put("city", "New York"); } catch (JSONException exception) { } user.setCustomData(jsonObject.toString()); QBUsers.updateUser(user).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // set user's tag or tags val userTags = StringifyArrayList<String>() userTags.add("tagOne") userTags.add("tagTwo") user.tags = userTags // set user's custom data val jsonObject = JSONObject() try { jsonObject.put("name", "John") jsonObject.put("age", 31) jsonObject.put("city", "New York") } catch (exception: JSONException) { } user.setCustomData(jsonObject.toString()) QBUsers.updateUser(user).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Field | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | tags | no | User tags. An array of Strings. A tag must include alphanumeric characters only and start with a letter. The tag must be at least 3 and no more than 15 characters long. There are no spaces in the tag format. For example, the "tagOne" format is correct while the "tag one" format is incorrect. If more that 10 tags are provided, an error is returned: tag list should contain maximum 10 tags. | | customData | no | User custom data. Should be a String. You can convert any data types to String, for example, JSON, XML , etc. | ## Set user avatar To set a user avatar, just create and upload a file to the QuickBlox cloud storage and connect it to the user. To create a file to QuickBlox cloud storage, you should overwrite the file from the device file system to a `java.io.File` and then write it to the `avatar`. Then, upload the file to the QuickBlox cloud storage by calling the `uploadFileTask()` method and passing the `avatar` to it. Now that the file is uploaded, get the ID of the uploaded file. See [this section](/sdks/android-content#upload-file) to learn more about file upload functionality in details. To connect the file to the user, set the ID of the uploaded file to the `fileId` field of the `user` and call the `updateUser()` method. As a result, the user avatar gets updated. <Tabs> <Tab title="Java"> ```Java theme={null} private void setAvatar() { // just as example for User model final QBUser user = new QBUser(); user.setId(123); String tags = null; // create file File avatar = new File("path to file"); // Some logic for write file by OutputStream and InputStream. // upload file to the server boolean fileIsPublic = false; QBContent.uploadFileTask(avatar, fileIsPublic, tags, new QBProgressCallback() { @Override public void onProgressUpdate(int i) { // do something to show progress if needed } }).performAsync(new QBEntityCallback<QBFile>() { @Override public void onSuccess(QBFile file, Bundle bundle) { int uploadedFileId = file.getId(); // add File to User user.setFileId(uploadedFileId); updateUser(user); } @Override public void onError(QBResponseException exception) { } }); } private void updateUser(QBUser user) { QBUsers.updateUser(user).performAsync(new QBEntityCallback<QBUser>() { @Override public void onSuccess(QBUser user, Bundle bundle) { } @Override public void onError(QBResponseException exception) { } }); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} private fun setAvatar() { // just as example for User model val user = QBUser() user.id = 123 val tags:String? = null // create file val avatar = File("path to file") // Some logic for write file by OutputStream and InputStream. // upload file to the server val fileIsPublic = false QBContent.uploadFileTask(avatar, fileIsPublic, tags) { // do something to show progress if needed }.performAsync(object : QBEntityCallback<QBFile> { override fun onSuccess(file: QBFile?, bundle: Bundle?) { val uploadedFileId = file?.id // add File to User user.fileId = uploadedFileId updateUser(user) } override fun onError(exception: QBResponseException?) { } }) } fun updateUser(user : QBUser) { QBUsers.updateUser(user).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { } override fun onError(exception: QBResponseException?) { } }) } ``` </Tab> </Tabs> Pass the following arguments to the `updateUser()` method: | Argument | Required | Description | | -------- | -------- | ----------------------------------------- | | user | yes | Specifies user fields that should be set. | ## Get user avatar Now, other users can get your avatar by calling the `downloadFileById()` method. <Tabs> <Tab title="Java"> ```Java theme={null} int userAvatarId = user.getFileId(); QBContent.downloadFileById(userAvatarId, new QBProgressCallback() { @Override public void onProgressUpdate(int i) { // do something to show progress if needed } }).performAsync(new QBEntityCallback<InputStream>() { @Override public void onSuccess(InputStream inputStream, Bundle bundle) { // receiving input stream // just write an InputStream to a file and show it } @Override public void onError(QBResponseException exceptione) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val userAvatarId = user.fileId QBContent.downloadFileById(userAvatarId) { // do something to show progress if needed }.performAsync(object : QBEntityCallback<InputStream> { override fun onSuccess(inputStream: InputStream?, bundle: Bundle?) { // receiving input stream // just write an InputStream to a file and show it } override fun onError(exception: QBResponseException?) { } }) ``` </Tab> </Tabs> | Argument | Required | Descriotion | | ------------ | -------- | ----------------------- | | userAvatarID | yes | File unique identifier. | # Basic Source: https://docs.quickblox.com/sdks/android-video-calling Learn how to add peer-to-peer video calls to your app. QuickBlox Video Calling API is built on top of [WebRTC](https://webrtc.org/). It allows adding real-time video communication features into your app similar to Skype using API easily. The communication is happening between peers representing camera devices. There are two **peer types**: * **Local peer** is a device running the app right now. * **Remote peer** is an opponent device. Establishing real-time video communication between two peers involves 3 phases: 1. **Signaling**. At this phase, the peers’ local IPs and ports where they can be reached (ICE candidates) are exchanged as well as their media capabilities and call session control messages. 2. **Discovery**. At this phase, the public IPs and ports at which endpoints can be reached are discovered by STUN/TURN server. 3. **Establishing a connection**. At this phase, the data are sent directly to each party of the communication process. <Warning> In order to start using Video Calling Module, you need to **connect to QuickBlox Chat first**. The signaling in the QuickBox WebRTC module is implemented over the XMPP protocol using QuickBlox Chat module. It acts as a signaling transport for Video Calling API. </Warning> <Note> Please use this WebRTC Video Calling to make the Group Calls with **4 or fewer users**. Because of [Mesh architecture](https://webrtcglossary.com/mesh/) we use for multi-point where every participant sends and receives its media to all other participants, the current solution supports group calls with up to 4 people. </Note> Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/android-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/android-authentication) page to learn how to do it. 4. Connect to the Chat server to provide a signaling mechanism for Video Calling API. Follow our [Chat](/sdks/android-chat) page to learn about chat connection settings and configuration. ## Initialize WebRTC To be able to receive incoming video chat calls, you should initialize `QBRTCClient` and add WebRTC signaling to it. <Tabs> <Tab title="Java"> ```Java theme={null} // add signalling manager chatService.getVideoChatWebRTCSignalingManager().addSignalingManagerListener(new QBVideoChatSignalingManagerListener() { @Override public void signalingCreated(QBSignaling signaling, boolean createdLocally) { if (!createdLocally) { rtcClient.addSignaling(signaling); } } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // add signalling manager chatService.videoChatWebRTCSignalingManager.addSignalingManagerListener { signaling, createdLocally -> if (!createdLocally) { rtcClient.addSignaling(signaling) } } // configure QBRTCConfig.setDebugEnabled(true) QBRTCConfig.setAnswerTimeInterval(answerTimeInterval) QBRTCConfig.setDisconnectTime(disconnectTimeInterval) QBRTCConfig.setDialingTimeInterval(dialingTimeInterval) ``` </Tab> </Tabs> <Warning> If you forget to set the Signaling Manager, you will not be able to process calls. </Warning> ## Manage calls Add `SessionCallbacksListener` to your `QBRTCClient` to define session states. Learn more details about the event listener configuration in the [Event listener](#event-listener) section. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCClient.getInstance(getApplicationContext()).addSessionCallbacksListener(new QBRTCClientSessionCallbacks() { @Override public void onReceiveNewSession(QBRTCSession session) { } @Override public void onUserNoActions(QBRTCSession session, Integer integer) { } @Override public void onSessionStartClose(QBRTCSession session) { } @Override public void onUserNotAnswer(QBRTCSession session, Integer integer) { } @Override public void onCallRejectByUser(QBRTCSession session, Integer integer, Map<String, String> map) { } @Override public void onCallAcceptByUser(QBRTCSession session, Integer integer, Map<String, String> map) { } @Override public void onReceiveHangUpFromUser(QBRTCSession session, Integer integer, Map<String, String> map) { } @Override public void onSessionClosed(QBRTCSession session) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBRTCClient.getInstance(applicationContext).addSessionCallbacksListener(object : QBRTCClientSessionCallbacks { override fun onReceiveNewSession(session: QBRTCSession?) { } override fun onUserNoActions(session: QBRTCSession?, integer: Int?) { } override fun onSessionStartClose(session: QBRTCSession?) { } override fun onUserNotAnswer(session: QBRTCSession?, integer: Int?) { } override fun onCallRejectByUser(session: QBRTCSession?, integer: Int?, map: Map<String, String>?) { } override fun onCallAcceptByUser(session: QBRTCSession?, integer: Int?, map: Map<String, String>?) { } override fun onReceiveHangUpFromUser(session: QBRTCSession?, integer: Int?, map: Map<String, String>?) { } override fun onSessionClosed(session: QBRTCSession?) { } }) ``` </Tab> </Tabs> <Tip> You can use two another сallbacks to add to `QBRTCClient`: * `QBRTCSessionEventsCallback` (with overriding methods) to handle session main events. * `QBRTCClientSessionCallbacksImpl` to make your own Session Callback Manager. </Tip> You should allow `QBRTCClient` to process calls. To be sure that your app is ready for calls processing and `Activity` exists, use the following snippet in the `Activity` class. <Tabs> <Tab title="Java"> ```Java theme={null} rtcClient.prepareToProcessCalls(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} rtcClient.prepareToProcessCalls() ``` </Tab> </Tabs> Now the application is ready for processing calls. ## Local/remote video view Set up two video chat layouts for remote and local video tracks to be able to show the video. * A **remote video track** represents a remote peer video stream from a remote camera app. Specify `userId` for the remote camera app of the remote peer. * A **local video track** represents a local peer video stream from a local camera app. Specify `userId` for the local camera app of the local peer. ```XML XML theme={null} <com.quickblox.videochat.webrtc.view.QBRTCSurfaceView android:id="@+id/remote_video_view" android:layout_width="match_parent" android:layout_height="match_parent"/> <com.quickblox.videochat.webrtc.view.QBRTCSurfaceView android:id="@+id/local_video_view" android:layout_width="match_parent" android:layout_height="match_parent"/> ``` `QBRTCSurfaceView` allows using several views on the screen layout and overlapping each other. This is a good feature for group video calls. `QBRTCSurfaceView` is a surface view (it extends `org.webrtc.SurfaceViewRenderer` class) that renders video track. It has its own lifecycle for rendering. It uses `init()` method for preparing to render and `release()` to release resources when the video track does not exist anymore. `QBRTCSurfaceView` is **automatically initialized** after the surface is created (in `surfaceCreated()` method callback). You can manually initialize `QBRTCSurfaceView` using `EGLContext` getting from `QBRTCClient`. Use this only when `Activity` is alive and GL resources exist. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCSurfaceView surfaceView = (QBRTCSurfaceView) findViewById(R.id.remote_video_view); EglBase eglContext = QBRTCClient.getInstance(getContext()).getEglContext(); surfaceView.init(eglContext.getEglBaseContext(), null); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val surfaceView: QBRTCSurfaceView = findViewById(R.id.remote_video_view) as QBRTCSurfaceView val eglContext = QBRTCClient.getInstance(applicationContext).eglContext surfaceView.init(eglContext.eglBaseContext, null) ``` </Tab> </Tabs> <Warning> It is allowed to call `init()` to reinitialize the view only after a previous `init()`/`release()` cycle. </Warning> Method `release()` should be called when video track is no more valid, for example, when you receive `onConnectionClosedForUser()` callback from `QBRTCSession` or when `QBRTCSession` is going to close. But you should call `release()` method before `Activity` is destroyed and while `EGLContext` is still valid. If you don't call this method, the GL resources might leak. Here are the few methods of the `QBRTCSurfaceView`: <Tabs> <Tab title="Java"> ```Java theme={null} // set if the video stream should be mirrored or not surfaceView.setMirror(true); // set how the video will fill the allowed layout area surfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); // request to invalidate view when something has changed surfaceView.requestLayout(); // releases all related GL resources surfaceView.release(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // set if the video stream should be mirrored or not surfaceView.setMirror(true) // set how the video will fill the allowed layout area surfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) // request to invalidate view when something has changed surfaceView.requestLayout() // releases all related GL resources surfaceView.release() ``` </Tab> </Tabs> To render received video track from an opponent, you should have `QBRTCSurfaceView`, `QBRTCVideoTrack` and use: <Tabs> <Tab title="Java"> ```Java theme={null} videoTrack.addRenderer(videoView); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} videoTrack.addRenderer(videoView) ``` </Tab> </Tabs> To stop rendering video track, you should simply use: <Tabs> <Tab title="Java"> ```Java theme={null} videoTrack.removeRenderer(videoTrack.getRenderer()); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} videoTrack.removeRenderer(videoTrack.renderer) ``` </Tab> </Tabs> ## Initiate a call To call other users, you should create a call session with the user first using `createNewSessionWithOpponents()` method. After that, you can start calling using `startCall()` method. <Tabs> <Tab title="Java"> ```Java theme={null} // create collection of opponents ID List<Integer> opponents = new ArrayList<>(); for (QBUser user : users) { opponents.add(user.getId()); } // you can set any string key and value in user info // then retrieve this data from sessions which is returned in callbacks // and parse them as you wish Map<String, String> userInfo = new HashMap<>(); userInfo.put("key", "value"); // there are two call types: Audio or Video Call QBRTCTypes.QBConferenceType conferenceType = QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_AUDIO; // or QBRTCTypes.QBConferenceType conferenceType = QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO; // init session QBRTCSession session = QBRTCClient.getInstance(this).createNewSessionWithOpponents(opponents, conferenceType); // start call session.startCall(userInfo); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // create collection of opponents ID val opponents = ArrayList<Int>() for (user in users) { opponents.add(user.id) } // you can set any string key and value in user info // then retrieve this data from sessions which is returned in callbacks // and parse them as you wish val userInfo = HashMap<String, String>() userInfo["key"] = "value" // there are two call types: Audio or Video Call val conferenceType = QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_AUDIO // or val conferenceType = QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO // init session val session = QBRTCClient.getInstance(this).createNewSessionWithOpponents(opponents, conferenceType) // start call session.startCall(userInfo) ``` </Tab> </Tabs> Now, your opponents will receive a call request callback `onReceiveNewSession()` via `QBRTCClientSessionCallbacks` (read above). <Tabs> <Tab title="Java"> ```Java theme={null} @Override public void onReceiveNewSession(QBRTCSession session) { Map<String, String> userInfo = new HashMap<>(); userInfo.put("key", "value"); session.acceptCall(userInfo); session.rejectCall(userInfo); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} override fun onReceiveNewSession(session: QBRTCSession?) { val userInfo = HashMap<String, String>() userInfo["key"] = "value" session.acceptCall(userInfo) session.rejectCall(userInfo) } ``` </Tab> </Tabs> ## Accept a call If you accept an incoming call, your opponent receives an `onCallAcceptByUser()` callback. <Tabs> <Tab title="Java"> ```Java theme={null} @Override public void onCallAcceptByUser(QBRTCSession session, Integer userId, Map<String, String> userInfo) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} override fun onCallAcceptByUser(session: QBRTCSession?, userId: Int?, userInfo: Map<String, String>?) { } ``` </Tab> </Tabs> ## Reject a call If you reject an incoming call, your opponent receives an `onCallRejectByUser()` callback. <Tabs> <Tab title="Java"> ```Java theme={null} @Override public void onCallRejectByUser(QBRTCSession session, Integer userId, Map<String, String> userInfo) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} override fun onCallRejectByUser(session: QBRTCSession?, userId: Int?, userInfo: Map<String, String>?) { } ``` </Tab> </Tabs> ## Ignore a call If you neither **accept** nor **reject** an incoming call, the caller will receive an appropriate callback after a specific period of time. You can set interval using `QBRTCConfig.setAnswerTimeInterval()` method. <Tabs> <Tab title="Java"> ```Java theme={null} @Override public void onUserNotAnswer(QBRTCSession session, Integer userId) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} override fun onUserNotAnswer(session: QBRTCSession?, userId: Int?) { } ``` </Tab> </Tabs> <Note> **Note** If you are already in the call, you still can receive other incoming requests. You can handle a **current call session** and use any logic you like to notify the user about incoming call attempts. </Note> ## Render video stream to view For managing video tracks, you should use the `QBRTCClientVideoTracksCallbacks` interface. <Tabs> <Tab title="Java"> ```Java theme={null} session.addVideoTrackCallbacksListener(this); session.removeVideoTrackCallbacksListener(this); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} session.addVideoTrackCallbacksListener(this) session.removeVideoTrackCallbacksListener(this) ``` </Tab> </Tabs> <Tabs> <Tab title="Java"> ```Java theme={null} new QBRTCClientVideoTracksCallbacks() { @Override public void onLocalVideoTrackReceive(BaseSession session, QBRTCVideoTrack videoTrack) { } @Override public void onRemoteVideoTrackReceive(BaseSession session, QBRTCVideoTrack videoTrack, Integer userId) { } } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} object : QBRTCClientVideoTracksCallbacks<QBRTCSession> { override fun onLocalVideoTrackReceive(session: QBRTCSession?, videoTrack: QBRTCVideoTrack?) { } override fun onRemoteVideoTrackReceive(session: QBRTCSession?, videoTrack: QBRTCVideoTrack?, userId: Int?) { } } ``` </Tab> </Tabs> Once you've got an access to video track, you can render them to some view in your app UI. <Tabs> <Tab title="Java"> ```Java theme={null} private void fillVideoView(QBRTCSurfaceView videoView, QBRTCVideoTrack videoTrack) { // to remove renderer if Video Track already has another one videoTrack.cleanUp(); if (videoView != null) { videoTrack.addRenderer(videoView); updateVideoView(videoView); } } private void updateVideoView(SurfaceViewRenderer videoView) { RendererCommon.ScalingType scalingType = RendererCommon.ScalingType.SCALE_ASPECT_FILL; videoView.setScalingType(scalingType); videoView.setMirror(false); videoView.requestLayout(); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} private fun fillVideoView(videoView: QBRTCSurfaceView?, videoTrack: QBRTCVideoTrack) { // To remove renderer if Video Track already has another one videoTrack.cleanUp() if (videoView != null) { videoTrack.addRenderer(videoView) updateVideoView(videoView) } } private fun updateVideoView(videoView: SurfaceViewRenderer) { val scalingType = RendererCommon.ScalingType.SCALE_ASPECT_FILL videoView.setScalingType(scalingType) videoView.setMirror(false) videoView.requestLayout() }} ``` </Tab> </Tabs> ## Obtain audio tracks For managing audio tracks, you should use the `QBRTCClientAudioTracksCallback` interface. <Tabs> <Tab title="Java"> ```Java theme={null} session.addAudioTrackCallbacksListener(this); session.removeAudioTrackCallbacksListener(this); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} session.addAudioTrackCallbacksListener(this) session.removeAudioTrackCallbacksListener(this) ``` </Tab> </Tabs> <Tabs> <Tab title="Java"> ```Java theme={null} new QBRTCClientAudioTracksCallback() { @Override public void onLocalAudioTrackReceive(BaseSession session, QBRTCAudioTrack audioTrack) { } @Override public void onRemoteAudioTrackReceive(BaseSession session, QBRTCAudioTrack audioTrack, Integer userId) { } } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} object : QBRTCClientAudioTracksCallback<QBRTCSession>{ override fun onLocalAudioTrackReceive(session: QBRTCSession?, audioTrack: QBRTCAudioTrack?) { } override fun onRemoteAudioTrackReceive(session: QBRTCSession?, audioTrack: QBRTCAudioTrack?, userId: Int?) { } } ``` </Tab> </Tabs> Then you can use these audio tracks to mute/unmute audio. ## End a call To end a call, use the`hangUp()`method. <Tabs> <Tab title="Java"> ```Java theme={null} Map<String, String> userInfo = new HashMap<>(); userInfo.put("key", "value"); session.hangUp(userInfo); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val userInfo = HashMap<String, String>() userInfo["key"] = "value" session.hangUp(userInfo) ``` </Tab> </Tabs> After this, the call session is going to close and your opponent will receive an `onReceiveHangUpFromUser()` callback. <Tabs> <Tab title="Java"> ```Java theme={null} @Override public void onReceiveHangUpFromUser(QBRTCSession session, Integer userId, Map<String, String> userInfo) { } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} override fun onReceiveHangUpFromUser(session: QBRTCSession?, userId: Int?, userInfo: Map<String, String>?) { } ``` </Tab> </Tabs> ## Release resource When you don't want to receive and process video calls, for example, when a user is logged out, you have to destroy `QBRTCClient`. Call the `destroy()` method to unregister from receiving any video chat events and close existing signaling channels. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCClient.getInstance(this).destroy(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBRTCClient.getInstance(this).destroy() ``` </Tab> </Tabs> ## Event listener To process events such as incoming call, call reject, hang up, etc. you need to set up the event listener. The event listener processes various events that happen with **call session** or **peer connection** in your app. Using the callbacks provided by the event listener, you can implement and execute the event-related processing code. For example, the `onCallAcceptByUser ` callback of `QBRTCClient` is received when a call has been accepted. This callback receives information about the call session, user ID who accepted the call, and additional key-value data about the user. QuickBlox Android SDK persistently interacts with the server via **XMPP** connection that works as a **signaling** transport for establishing a call between two or more peers. It receives the callbacks of the asynchronous events which happen with the call and peer connection. This allows you to track these events and build your own video calling features around them. To track call session events, you should use `SessionCallbacksListener`. The inherited event listeners for a call session are `QBRTCClientSessionCallback` and `QBRTCSessionConnectionCallbacks`. **QBRTCClientSessionCallback** The supported call session event callbacks of `QBRTCClientSessionCallback` along with their parameters as well as shows how to add the listener. | Method | Invoked when | | ------------------------- | ----------------------------------------------------------------------------------- | | onReceiveNewSession() | A new call session has been received. | | onCallAcceptByUser() | A call session has been accepted. | | onCallRejectByUser() | A call session has been rejected. | | onReceiveHangUpFromUser() | An accepted call session has been ended by the peer by pressing the hang-up button. | | onUserNotAnswer() | A remote peer did not respond to your call within the timeout period. | | onUserNoActions() | A user didn't take any actions on the received call session. | | onSessionStartClose() | A call session is going to be closed. | | onSessionClosed() | A call session has been closed. | The following code lists all supported event callbacks for a call session along with their parameters as well as shows how to add the listener. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCClient.getInstance(this).addSessionCallbacksListener(new QBRTCClientSessionCallbacks() { @Override public void onReceiveNewSession(QBRTCSession session) { } @Override public void onUserNoActions(QBRTCSession session, Integer userId) { } @Override public void onSessionStartClose(QBRTCSession session) { } @Override public void onUserNotAnswer(QBRTCSession session, Integer userId) { } @Override public void onCallRejectByUser(QBRTCSession session, Integer userID, Map<String, String> userInfo) { } @Override public void onCallAcceptByUser(QBRTCSession session, Integer userID, Map<String, String> userInfo) { } @Override public void onReceiveHangUpFromUser(QBRTCSession session, Integer userID, Map<String, String> userInfo) { } @Override public void onSessionClosed(QBRTCSession session) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBRTCClient.getInstance(this).addSessionCallbacksListener(object : QBRTCClientSessionCallbacks { override fun onReceiveNewSession(session: QBRTCSession?) { } override fun onUserNoActions(session: QBRTCSession?, userId: Int?) { } override fun onSessionStartClose(session: QBRTCSession?) { } override fun onUserNotAnswer(session: QBRTCSession?, userId: Int?) { } override fun onCallRejectByUser(session: QBRTCSession?, userId: Int?, userInfo: MutableMap<String, String>?) { } override fun onCallAcceptByUser(session: QBRTCSession?, userId: Int?, userInfo: MutableMap<String, String>?) { } override fun onReceiveHangUpFromUser(session: QBRTCSession?, userId: Int?, userInfo: MutableMap<String, String>?) { } override fun onSessionClosed(session: QBRTCSession?) { } }) ``` </Tab> </Tabs> Go to the [Resources](#resources) section to see a sequence diagram for a regular call workflow. **QBRTCSessionConnectionCallbacks** The supported call session event callbacks of `QBRTCSessionConnectionCallbacks` listener are listed in the table below. | Method | Invoked when | | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | onStateChanged | A call session connection state has been changed in real-time. View all available call session states in the [Call session states](#call-session-states) section. | | onStartConnectToUser | A connection establishment process has been started. | | onConnectedToUser | A peer connection has been established. | | onConnectionFailedWithUser | A peer connection has failed. | | onDisconnectedFromUser | A connection was terminated. | | onDisconnectedTimeoutFromUser | An opponent has been disconnected by timeout. | | onConnectionClosedForUser | A connection has been closed for the user. | The following code lists all supported event callbacks for a call session along with their parameters as well as shows how to add the listener. <Tabs> <Tab title="Java"> ```Java theme={null} session.addSessionCallbacksListener(new QBRTCSessionConnectionCallbacks() { @Override public void onStartConnectToUser(QBRTCSession session, Integer userId) { } @Override public void onDisconnectedTimeoutFromUser(QBRTCSession session, Integer userId) { } @Override public void onConnectionFailedWithUser(QBRTCSession session, Integer userId) { } @Override public void onStateChanged(QBRTCSession session, BaseSession.QBRTCSessionState sessionState) { } @Override public void onConnectedToUser(QBRTCSession session, Integer userId) { } @Override public void onDisconnectedFromUser(QBRTCSession session, Integer userId) { } @Override public void onConnectionClosedForUser(QBRTCSession session, Integer userId) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} session.addSessionCallbacksListener(object : QBRTCSessionConnectionCallbacks { override fun onStartConnectToUser(session: QBRTCSession?, userId: Int?) { } override fun onDisconnectedTimeoutFromUser(session: QBRTCSession?, userId: Int?) { } override fun onConnectionFailedWithUser(session: QBRTCSession?, userId: Int?) { } override fun onStateChanged(session: QBRTCSession?, sessionState: BaseSession.QBRTCSessionState?) { } override fun onConnectedToUser(session: QBRTCSession?, userId: Int?) { } override fun onDisconnectedFromUser(session: QBRTCSession?, userId: Int?) { } override fun onConnectionClosedForUser(session: QBRTCSession?, userId: Int?) { } }) ``` </Tab> </Tabs> Go to the [Resources](#resources) section to see a sequence diagram for a regular call workflow. ### Call session states The following table lists all supported call session connection states. | State | Description | | ---------------------------------- | -------------------------------------------------------------------- | | QB\_RTC\_SESSION\_NEW | A call session was successfully created and ready for the next step. | | QB\_RTC\_SESSION\_PENDING | A call session is in a pending state for other actions to occur. | | QB\_RTC\_SESSION\_CONNECTING | The call session is in the progress of establishing a connection. | | QB\_RTC\_SESSION\_GOING\_TO\_CLOSE | A call session is going to be closed. | | QB\_RTC\_SESSION\_CLOSED | A call session has been closed. | ### Peer connection states To get peer connection state for a particular peer, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCSession session = WebRtcSessionManager.getInstance(getApplicationContext()).getCurrentSession(); QBRTCTypes.QBRTCConnectionState peerConnectionState = session.getPeerConnection(userId).getState(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val session = WebRtcSessionManager.getInstance(applicationContext).currentSession val peerConnectionState = session.getPeerConnection(userId).state ``` </Tab> </Tabs> The following table lists all supported peer connection states. | State | Description | | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | QB\_RTC\_CONNECTION\_UNKNOWN | A peer connection state is unknown. This can occur when none of the other states are fit for the current situation. | | QB\_RTC\_CONNECTION\_NEW | A peer connection has been created and has not done any networking yet. | | QB\_RTC\_CONNECTION\_WAIT | A peer connection is in a waiting state. | | QB\_RTC\_CONNECTION\_PENDING | A peer connection is in a pending state for other actions to occur. | | QB\_RTC\_CONNECTION\_CONNECTING | One or more of the ICE transports are currently in the process of establishing a connection. | | QB\_RTC\_CONNECTION\_CHECKING | The ICE agent has been given one or more remote candidates and is checking pairs of local and remote candidates against one another to try to find a compatible match, but has not yet found a pair which will allow the peer connection to be made. It is possible that the gathering of candidates is also still underway. | | QB\_RTC\_CONNECTION\_CONNECTED | A usable pairing of local and remote candidates has been found for all components of the connection, and the connection has been established. | | QB\_RTC\_CONNECTION\_DISCONNECTED | A peer has been disconnected from the call session. But the call session is still open and the peer can be reconnected to the call session. | | QB\_RTC\_CONNECTION\_DISCONNECT\_TIMEOUT | The peer connection was disconnected by the timeout. | | QB\_RTC\_CONNECTION\_CLOSED | A peer connection was closed. But the call session can still be open because there can several peer connections in a single call session. The ICE agent for this peer connection has shut down and is no longer handling requests. | | QB\_RTC\_CONNECTION\_NOT\_ANSWER | No answer received from the remote peer. | | QB\_RTC\_CONNECTION\_REJECT | An incoming call has been rejected by the remote peer without accepting the call. | | QB\_RTC\_CONNECTION\_HANG\_UP | The connection was hung up by the remote peer. | | QB\_RTC\_CONNECTION\_FAILED | One or more of the ICE transports on the connection is in the failed state. This can occur in different circumstances, for example, bad network, etc. | | QB\_RTC\_CONNECTION\_ERROR | A peer connection has an error. | ## Resources A regular call workflow. <Frame> <img alt="Android SDK Initiate, Accept, Hang Up Call" /> </Frame> # Advanced Source: https://docs.quickblox.com/sdks/android-video-calling-advanced Learn how to mute audio, disable video, switch camera, share your screen, configure media settings, etc. ## Media management To manage audio & video streams `QBRTCSession` provides `QBMediaStreamManager` class. `QBMediaStreamManager` holds a user's **local audio & video tracks** and provides a way to change the video capturer. <Note> `QBMediaStreamManager` is attached to `QBRTCSession` lifecycle. According to `QBRTCSession` lifecycle, you should use `QBMediaStreamManager` only when `QBRTCSession` is active or has been started. </Note> ## Mute audio Mute the audio by calling `setEnabled()` or `setAudioEnabled()` method. Using these methods, we can tell SDK to send/not send audio data either from a local or remote peer in the specified call session. <Tabs> <Tab title="Java"> ```Java theme={null} QBMediaStreamManager mediaStreamManager = currentSession.getMediaStreamManager(); QBRTCAudioTrack localAudioTrack = mediaStreamManager.getLocalAudioTrack(); // mute localAudioTrack.setEnabled(false); // or mediaStreamManager.setAudioEnabled(false); // unmute localAudioTrack.setEnabled(true); // or mediaStreamManager.setAudioEnabled(true); // is muted? boolean isEnabled = localAudioTrack.enabled(); // or mediaStreamManager.isAudioEnabled(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val mediaStreamManager = currentSession.mediaStreamManager val localAudioTrack = mediaStreamManager.localAudioTrack // mute localAudioTrack.setEnabled(false) // or mediaStreamManager.isAudioEnabled = false // unmute localAudioTrack.setEnabled(true) // or mediaStreamManager.isAudioEnabled = true // is muted? val isEnabled = localAudioTrack.enabled() // or mediaStreamManager.isAudioEnabled ``` </Tab> </Tabs> ## Disable video Turn off the video by calling `setEnabled()` or `setVideoEnabled()` method. Using these methods, we can tell SDK not to send video data either from a local or remote peer in the specified call session. <Tabs> <Tab title="Java"> ```Java theme={null} QBMediaStreamManager mediaStreamManager = currentSession.getMediaStreamManager(); QBRTCVideoTrack localVideoTrack = mediaStreamManager.getLocalVideoTrack(); // enable localVideoTrack.setEnabled(false); // or mediaStreamManager.setVideoEnabled(false); // disable localVideoTrack.setEnabled(true); // or mediaStreamManager.setVideoEnabled(true); // is enabled? boolean isEnabled = localVideoTrack.enabled(); // or mediaStreamManager.isVideoEnabled(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val mediaStreamManager = currentSession.mediaStreamManager val localVideoTrack = mediaStreamManager.localVideoTrack // enable localVideoTrack.setEnabled(false) // or mediaStreamManager.isVideoEnabled = false // disable localVideoTrack.setEnabled(true) // or mediaStreamManager.isVideoEnabled = true // is Enabled? val isEnabled = localVideoTrack.enabled() // or mediaStreamManager.isVideoEnabled ``` </Tab> </Tabs> ## Capture video from camera When a call session is started, the Camera Capturer is used by default. If you want to use it manually, you should set `QBRTCCameraVideoCapturer` as Video Capturer. <Tabs> <Tab title="Java"> ```java theme={null} try { currentSession.getMediaStreamManager().setVideoCapturer(new QBRTCCameraVideoCapturer(context, null)); } catch (QBRTCCameraVideoCapturer.QBRTCCameraCapturerException exception) { exception.printStackTrace(); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} try { currentSession.mediaStreamManager.videoCapturer = QBRTCCameraVideoCapturer(context, null) } catch (exception: QBRTCCameraVideoCapturer.QBRTCCameraCapturerException) { exception.printStackTrace() } ``` </Tab> </Tabs> <Warning> Creating a new instance of `QBRTCCameraVideoCapturer` throws the `QBRTCCameraCapturerException` so you should handle this exception. </Warning> ## Switch camera You can switch the video camera during a call. (Default: **front camera**) <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCCameraVideoCapturer videoCapturer = (QBRTCCameraVideoCapturer) currentSession.getMediaStreamManager().getVideoCapturer(); videoCapturer.switchCamera(cameraSwitchHandler); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val videoCapturer = currentSession.mediaStreamManager.videoCapturer as QBRTCCameraVideoCapturer videoCapturer.switchCamera(cameraSwitchHandler) ``` </Tab> </Tabs> You should use `CameraSwitchHandler` to handle the camera switching process. <Tabs> <Tab title="Java"> ```Java theme={null} CameraVideoCapturer.CameraSwitchHandler cameraSwitchHandler = new CameraVideoCapturer.CameraSwitchHandler() { @Override public void onCameraSwitchDone(boolean switched) { } @Override public void onCameraSwitchError(String message) { } }; ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val cameraSwitchHandler = object : CameraVideoCapturer.CameraSwitchHandler { override fun onCameraSwitchDone(switched: Boolean?) { } override fun onCameraSwitchError(message: String?) { } } ``` </Tab> </Tabs> ## Change capture format You can change framerate and frame size during an active call session using `videoCapturer`. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCCameraVideoCapturer videoCapturer = (QBRTCCameraVideoCapturer) currentSession.getMediaStreamManager().getVideoCapturer(); videoCapturer.changeCaptureFormat(width, height, framerate); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val videoCapturer = currentSession.mediaStreamManager.videoCapturer as QBRTCCameraVideoCapturer videoCapturer.changeCaptureFormat(width, height, framerate) ``` </Tab> </Tabs> ## Screen sharing To share the screen of your device with the opponents, follow the steps below: 1. Ask appropriate permission. <Tabs> <Tab title="Java"> ```Java theme={null} if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { QBRTCScreenCapturer.requestPermissions(CallActivity.this); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { QBRTCScreenCapturer.requestPermissions(this@CallActivity) } ``` </Tab> </Tabs> <Warning> Instead of `CallActivity.this`, you can use the context of the activity where you are asking this permission. </Warning> 1. Handle results of asking the permission. <Tabs> <Tab title="Java"> ```Java theme={null} @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (requestCode == QBRTCScreenCapturer.REQUEST_MEDIA_PROJECTION) { if (resultCode == Activity.RESULT_OK) { // now you can start Screen Sharing startScreenSharing(data); } } } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == QBRTCScreenCapturer.REQUEST_MEDIA_PROJECTION) { if (resultCode == Activity.RESULT_OK) { // now you can start Screen Sharing startScreenSharing(data) } } } ``` </Tab> </Tabs> <Warning> You should pass the Intent (data) to `startScreenSharing()` method to use it for `setVideoCapturer()`. </Warning> 1. Set `QBRTCScreenCapturer` as Video Capturer. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCSession currentSession = getCurrentSession(); // simply use your current session variable currentSession.getMediaStreamManager().setVideoCapturer(new QBRTCScreenCapturer(data, null)); // data - it's Intent from onActivityResult ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val currentSession = getCurrentSession() // Simply use your current session variable currentSession.getMediaStreamManager().setVideoCapturer(QBRTCScreenCapturer(data, null)) // data - it's Intent from onActivityResult ``` </Tab> </Tabs> ## WebRTC stats reporting You are able to receive an information report about the current connection, audio, video tracks, and other useful information. To set a receiving time interval, use the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCConfig.setStatsReportInterval(60); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} QBRTCConfig.setStatsReportInterval(60) ``` </Tab> </Tabs> Then you should use the `QBRTCStatsReportCallback` and add them. <Tabs> <Tab title="Java"> ```Java theme={null} currentSession.addStatsReportCallback(new QBRTCStatsReportCallback() { @Override public void onStatsReportUpdate(QBRTCStatsReport statsReport, Integer userId) { statsReport.getAudioReceivedCodec(); statsReport.getAudioReceivedBitrate(); statsReport.getVideoSendCodec(); statsReport.getVideoSendBitrate(); statsReport.getVideoReceivedBitrate(); statsReport.getVideoReceivedFps(); statsReport.getAudioSendInputLevel(); statsReport.getVideoReceivedWidth(); statsReport.getVideoReceivedHeight(); } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} currentSession.addStatsReportCallback { statsReport, userId -> statsReport.audioReceivedCodec statsReport.audioReceivedBitrate statsReport.videoSendCodec statsReport.videoSendBitrate statsReport.videoReceivedBitrate statsReport.videoReceivedFps statsReport.audioSendInputLevel statsReport.videoReceivedWidth statsReport.videoReceivedHeight } ``` </Tab> </Tabs> <Note> The `qbrtcStatsReport.audioReceivedCodec()` and `qbrtcStatsReport.videoReceivedFps()` are not all you can get from the `QBRTCStatsReport`. This is just the example. </Note> <Tip> Using `QBRTCStatsReport`, you can define when your opponent is speaking by using the `qbrtcStatsReport.getAudioReceiveOutputLevel()` parameter. This parameter is the **microphone level** from the participant’s **audio track** at the moment of collecting the statistics report. </Tip> ## General settings You can change different settings for your calls using the `QBRTCConfig` class. All of them are listed below. ### Answer time interval If an opponent hasn't answered you within an answer time interval, then the `onUserNotAnswer()` callback will be returned. The answer time interval shows how much time an opponent has to answer your call. Set the answer time interval using the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} // time interval to wait opponents answer QBRTCConfig.setAnswerTimeInterval(answerTimeInterval); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // time interval to wait opponents answer QBRTCConfig.setAnswerTimeInterval(answerTimeInterval) ``` </Tab> </Tabs> <Note> **By default**, the answer time interval is 60 seconds. </Note> ### Disconnect time interval Set maximum allowed time to repair a connection after it was lost. Set the disconnect time interval using the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} // time to repair the connection after it was lost QBRTCConfig.setDisconnectTime(disconnectTimeInterval); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // time to repair the connection after it was lost QBRTCConfig.setDisconnectTime(disconnectTimeInterval) ``` </Tab> </Tabs> <Note> **By default**, the disconnect time interval is 10 seconds. </Note> ### Dialing time interval Dialing time interval indicates how often to notify your opponents about your call. Set the dialing time interval using the code snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} // time interval for establishing connection with the opponent QBRTCConfig.setDialingTimeInterval(dialingTimeInterval); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // time interval for establishing connection with the opponent QBRTCConfig.setDialingTimeInterval(dialingTimeInterval) ``` </Tab> </Tabs> <Note> **By default**, the dialing time interval is 5 seconds. </Note> ### Maximum number of opponents Set the maximum number of opponents in a group call using the snippet below. <Tabs> <Tab title="Java"> ```Java theme={null} // max number of opponents in group call QBRTCConfig.setMaxOpponentsCount(maxOpponentCount); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} // max number of opponents in group call QBRTCConfig.setMaxOpponentsCount(maxOpponentCount) ``` </Tab> </Tabs> <Note> **By default**, the maximum number of opponents is 10. </Note> ### Custom ICE servers You can customize a list of ICE servers. **By default**, WebRTC module will use internal ICE servers that are usually enough, but you can always set your own. WebRTC engine will choose the TURN relay with the lowest round-trip time. Thus, setting multiple TURN servers allows your application to scale-up in terms of bandwidth and number of users. Review our [Setup](/sdks/android-setup#custom-ice-servers) guide to learn how to configure custom ICE servers. ## Media settings You can use the `QBRTCMediaConfig` class instance to configure a variety of media settings such as video/audio codecs, bitrate, fps, etc. ### Video codecs It's possible to set a video codec. You can choose from the following values: **H264**, **VP8**, and **VP9**. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCMediaConfig.VideoCodec videoCodec; videoCodec = QBRTCMediaConfig.VideoCodec.H264; videoCodec = QBRTCMediaConfig.VideoCodec.VP8; videoCodec = QBRTCMediaConfig.VideoCodec.VP9; QBRTCMediaConfig.setVideoCodec(videoCodec); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} var videoCodec: QBRTCMediaConfig.VideoCodec videoCodec = QBRTCMediaConfig.VideoCodec.H264 videoCodec = QBRTCMediaConfig.VideoCodec.VP8 videoCodec = QBRTCMediaConfig.VideoCodec.VP9 QBRTCMediaConfig.setVideoCodec(videoCodec) ``` </Tab> </Tabs> ### Camera resolution You can also set the custom video resolution to provide guarantees for the predictable behavior of the video stream. <Tabs> <Tab title="Java"> ```Java theme={null} int videoWidth = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.width; int videoHeight = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.height; // VGA Resolution //videoWidth = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.width; //videoHeight = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.height; // HD Resolution //videoWidth = QBRTCMediaConfig.VideoQuality.HD_VIDEO.width; //videoHeight = QBRTCMediaConfig.VideoQuality.HD_VIDEO.height; // custom Resolution (for example FullHD) //videoWidth = 1920; //videoHeight = 1080; QBRTCMediaConfig.setVideoWidth(videoWidth); QBRTCMediaConfig.setVideoHeight(videoHeight); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} var videoWidth = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.width var videoHeight = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.height // VGA Resolution //videoWidth = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.width //videoHeight = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.height // HD Resolution //videoWidth = QBRTCMediaConfig.VideoQuality.HD_VIDEO.width //videoHeight = QBRTCMediaConfig.VideoQuality.HD_VIDEO.height // Custom Resolution (for example FullHD) //videoWidth = 1920 //videoHeight = 1080 QBRTCMediaConfig.setVideoWidth(videoWidth) QBRTCMediaConfig.setVideoHeight(videoHeight) ``` </Tab> </Tabs> ### Audio codecs Set an audio codec using the snippet below. You can choose from the following values: **ISAC** and **OPUS**. Default: **ISAC**. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCMediaConfig.AudioCodec audioCodec; audioCodec = QBRTCMediaConfig.AudioCodec.OPUS; audioCodec = QBRTCMediaConfig.AudioCodec.ISAC; QBRTCMediaConfig.setAudioCodec(audioCodec) ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} var audioCodec: QBRTCMediaConfig.AudioCodec audioCodec = QBRTCMediaConfig.AudioCodec.OPUS audioCodec = QBRTCMediaConfig.AudioCodec.ISAC QBRTCMediaConfig.setAudioCodec(audioCodec) ``` </Tab> </Tabs> ### Bitrate It's possible to set the custom bitrate to provide guarantees for the predictable behavior of the video stream. <Tabs> <Tab title="Java"> ```Java theme={null} int startBitrate = 0; //startBitrate = 2000; QBRTCMediaConfig.setVideoStartBitrate(startBitrate); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} var startBitrate: Int = 0 //startBitrate = 2000 QBRTCMediaConfig.setVideoStartBitrate(startBitrate) ``` </Tab> </Tabs> ### Hardware acceleration Enable hardware acceleration if the device supports it. Default: **false**. <Tabs> <Tab title="Java"> ```Java theme={null} boolean useHWAcceleration = true; QBRTCMediaConfig.setVideoHWAcceleration(useHWAcceleration); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val useHWAcceleration = true QBRTCMediaConfig.setVideoHWAcceleration(useHWAcceleration) ``` </Tab> </Tabs> ### Frames per second It's possible to set the custom fps to provide guarantees for the predictable behavior of the video stream. <Tabs> <Tab title="Java"> ```Java theme={null} int fps = 30; //fps = 30; QBRTCMediaConfig.setVideoFps(fps); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val fps = 30 //fps = 30; QBRTCMediaConfig.setVideoFps(fps) ``` </Tab> </Tabs> ### Acoustic echo cancellation Enable a built-in acoustic echo cancellation if the device supports it. Default: **true**. <Tabs> <Tab title="Java"> ```Java theme={null} boolean useAEC = true; QBRTCMediaConfig.setUseBuildInAEC(useAEC); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val useAEC = true QBRTCMediaConfig.setUseBuildInAEC(useAEC) ``` </Tab> </Tabs> ### Open sound library for embedded systems Enable open sound library for embedded systems (OpenSL ES audio) if the device supports it. Default: **false**. <Tabs> <Tab title="Java"> ```Java theme={null} boolean useOpenSLES = true; QBRTCMediaConfig.setUseOpenSLES(useOpenSLES); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val useOpenSLES = true QBRTCMediaConfig.setUseOpenSLES(useOpenSLES) ``` </Tab> </Tabs> ### Audio processing Enable audio processing if the device supports it. Default: **true**. <Tabs> <Tab title="Java"> ```Java theme={null} boolean useAudioProcessing = true; QBRTCMediaConfig.setAudioProcessingEnabled(useAudioProcessing); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val useAudioProcessing = true QBRTCMediaConfig.setAudioProcessingEnabled(useAudioProcessing) ``` </Tab> </Tabs> # Video Conference Source: https://docs.quickblox.com/sdks/android-video-conference Learn how to add video conference calls to your app. <Warning> This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com). </Warning> QuickBlox provides a Multiparty Video Conferencing solution allowing to set up a video conference between 10-12 people. It is built on top of [WebRTC SFU](https://webrtcglossary.com/sfu/) technologies. Features supported: * Video/Audio Conference with 10-12 people. * Join/Rejoin video room functionality (like Skype). * Mute/Unmute audio/video stream (own and opponents). * Display bitrate. * Switch video input device (camera). ## Initialize In order to start working with Multiparty Video Conferencing API, you need to initialize a conference endpoint. To request the conference server URL, please contact us:[enterprise@quickblox.com](mailto:enterprise@quickblox.com.). <Tabs> <Tab title="Java"> ```Java theme={null} String conferenceServer = "your_conference_server"; ConferenceConfig.setUrl(conferenceServer); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val conferenceServer = "your_conference_server" ConferenceConfig.setUrl(conferenceServer) ``` </Tab> </Tabs> | Argument | Required | Description | | ---------------- | -------- | ------------------------ | | conferenceServer | yes | A conference server URL. | ## Create session Create a session using `createSession()` method. `ConferenceClient` instance is a client model responsible for managing the conference session. `ConferenceClient` has a `setAutoSubscribeAfterJoin` option signifying that your client will be subscribed to all online publishers after joining some room. **By default**, `setAutoSubscribeAfterJoin` is `true`. `ConferenceSession` is a session within a certain video room, managing all current processes. <Tabs> <Tab title="Java"> ```Java theme={null} ConferenceClient client = ConferenceClient.getInstance(getApplicationContext()); QBRTCTypes.QBConferenceType conferenceType = isVideoCall ? QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO : QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_AUDIO; client.setAutoSubscribeAfterJoin(true); client.createSession(userId, conferenceType, new ConferenceEntityCallback<ConferenceSession>() { @Override public void onSuccess(ConferenceSession conferenceSession) { // session Created Successfully } @Override public void onError(WsException exception) { // create Session Error } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val client = ConferenceClient.getInstance(applicationContext) val conferenceType = if (isVideoCall) { QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO } else { QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_AUDIO } client.isAutoSubscribeAfterJoin = true client.createSession(userId, conferenceType, object : ConferenceEntityCallback<ConferenceSession> { override fun onSuccess(conferenceSession: ConferenceSession?) { // session Created Successfully } override fun onError(exception: WsException?) { // create Session Error } }) ``` </Tab> </Tabs> ## Callbacks To have the ability to receive callbacks about the current `ConferenceSession` instance state and conference events, you should implement appropriate interfaces. Implement the `QBRTCSessionStateCallback` for tracking connection state: <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCSessionStateCallback sessionStateCallback = new QBRTCSessionStateCallback<ConferenceSession>() { @Override public void onStateChanged(ConferenceSession conferenceSession, BaseSession.QBRTCSessionState sessionState) { } @Override public void onConnectedToUser(ConferenceSession conferenceSession, Integer userId) { } @Override public void onDisconnectedFromUser(ConferenceSession conferenceSession, Integer userId) { } @Override public void onConnectionClosedForUser(ConferenceSession conferenceSession, Integer userId) { } }; currentConferenceSession.addSessionCallbacksListener(sessionStateCallback); // or currentConferenceSession.removeSessionCallbacksListener(sessionStateCallback); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val sessionStateCallback = object : QBRTCSessionStateCallback<ConferenceSession> { override fun onStateChanged(conferenceSession: ConferenceSession?, sessionState: BaseSession.QBRTCSessionState?) { } override fun onConnectedToUser(conferenceSession: ConferenceSession?, userId: Int?) { } override fun onDisconnectedFromUser(conferenceSession: ConferenceSession?, userId: Int?) { } override fun onConnectionClosedForUser(conferenceSession: ConferenceSession?, userId: Int?) { } } currentConferenceSession.addSessionCallbacksListener(sessionStateCallback) // or currentConferenceSession.removeSessionCallbacksListener(sessionStateCallback) ``` </Tab> </Tabs> Implement `ConferenceSessionCallbacks` for tracking conference events: <Tabs> <Tab title="Java"> ```Java theme={null} ConferenceSessionCallbacks conferenceSessionCallbacks = new ConferenceSessionCallbacks() { @Override public void onPublishersReceived(ArrayList<Integer> publishersList) { // publisher or Publishers (users) joined } @Override public void onPublisherLeft(Integer userId) { // publisher left } @Override public void onMediaReceived(String type, boolean success) { // media received (audio or video type) } @Override public void onSlowLinkReceived(boolean uplink, int nacks) { // called when slowLink is received from server. // slowLink with uplink = true - several missing packets from server; // uplink = false means server is not receiving all your packets. } @Override public void onError(WsException exception) { // error from Server Received } @Override public void onSessionClosed(ConferenceSession conferenceSession) { // cession Closed } }; currentConferenceSession.addConferenceSessionListener(conferenceSessionCallbacks); // or currentConferenceSession.removeConferenceSessionListener(conferenceSessionCallbacks); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val conferenceSessionCallbacks = object : ConferenceSessionCallbacks { override fun onPublishersReceived(publishersList: ArrayList<Int>?) { // publisher or Publishers (users) joined } override fun onPublisherLeft(userId: Int?) { // publisher left } override fun onMediaReceived(type: String?, success: Boolean) { // media received (audio or video type) } override fun onSlowLinkReceived(uplink: Boolean, nacks: Int) { // called when slowLink is received from server. // slowLink with uplink = true - several missing packets from server; // uplink = false means server is not receiving all your packets. } override fun onError(exception: WsException?) { // error from Server Received } override fun onSessionClosed(conferenceSession: ConferenceSession?) { // cession Closed } } currentConferenceSession.addConferenceSessionListener(conferenceSessionCallbacks) // or currentConferenceSession.removeConferenceSessionListener(conferenceSessionCallbacks) ``` </Tab> </Tabs> ## Video and audio tracks For obtaining video and audio tracks implement interface `RTCClientVideoTracksCallbacks` and `RTCClientAudioTracksCallback`. <Tabs> <Tab title="Java"> ```Java theme={null} QBRTCClientVideoTracksCallbacks videoTracksCallbacks = new QBRTCClientVideoTracksCallbacks<ConferenceSession>() { @Override public void onLocalVideoTrackReceive(ConferenceSession conferenceSession, QBRTCVideoTrack videoTrack) { } @Override public void onRemoteVideoTrackReceive(ConferenceSession conferenceSession, QBRTCVideoTrack videoTrack, Integer userId) { } }; // add callback currentConferenceSession.addVideoTrackCallbacksListener(videoTracksCallbacks); // or remove currentConferenceSession.removeVideoTrackCallbacksListener(videoTracksCallbacks); QBRTCClientAudioTracksCallback audioTracksCallback = new QBRTCClientAudioTracksCallback<ConferenceSession>() { @Override public void onLocalAudioTrackReceive(ConferenceSession conferenceSession, QBRTCAudioTrack audioTrack) { } @Override public void onRemoteAudioTrackReceive(ConferenceSession conferenceSession, QBRTCAudioTrack audioTrack, Integer userId) { } }; // add callback currentConferenceSession.addAudioTrackCallbacksListener(audioTracksCallback); // or remove currentConferenceSession.removeAudioTrackCallbacksListener(audioTracksCallback); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val videoTracksCallbacks = object : QBRTCClientVideoTracksCallbacks<ConferenceSession> { override fun onLocalVideoTrackReceive(conferenceSession: ConferenceSession?, videoTrack: QBRTCVideoTrack?) { } override fun onRemoteVideoTrackReceive(conferenceSession: ConferenceSession?, videoTrack: QBRTCVideoTrack?, userId: Int?) { } } // add callback currentConferenceSession.addVideoTrackCallbacksListener(videoTracksCallbacks) // or remove currentConferenceSession.removeVideoTrackCallbacksListener(videoTracksCallbacks) val audioTracksCallback = object : QBRTCClientAudioTracksCallback<ConferenceSession> { override fun onLocalAudioTrackReceive(conferenceSession: ConferenceSession?, audioTrack: QBRTCAudioTrack?) { } override fun onRemoteAudioTrackReceive(conferenceSession: ConferenceSession?, audioTrack: QBRTCAudioTrack?, userId: Int?) { } } // Add callback currentConferenceSession.addAudioTrackCallbacksListener(audioTracksCallback) // Or remove currentConferenceSession.removeAudioTrackCallbacksListener(audioTracksCallback) ``` </Tab> </Tabs> To render the video track, use the `QBConferenceSurfaceView` as renderer: <Tabs> <Tab title="Java"> ```Java theme={null} QBConferenceSurfaceView videoView = findViewById(R.id.opponentView); videoTrack.removeRenderer(videoTrack.getRenderer()); videoTrack.addRenderer(videoView); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val videoView = findViewById<QBConferenceSurfaceView>(R.id.opponentView) qbrtcVideoTrack.removeRenderer(qbrtcVideoTrack.renderer) qbrtcVideoTrack.addRenderer(videoView) ``` </Tab> </Tabs> ## Join video room You can join a room as a listener or as a publisher. As a listener, you subscribe only to the publishers, without giving your own video and audio streams. <Tabs> <Tab title="Java"> ```Java theme={null} boolean asPublisher = true; int userId = currentConferenceSession.getCurrentUserID(); QBConferenceRole conferenceRole = asPublisher ? QBConferenceRole.PUBLISHER : QBConferenceRole.LISTENER; currentConferenceSession.joinDialog(dialogId, conferenceRole, new ConferenceEntityCallback<ArrayList<Integer>>() { @Override public void onSuccess(ArrayList<Integer> publishers) { } @Override public void onError(WsException exception) { } }); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val asPublisher = true val userId = currentConferenceSession.currentUserID val conferenceRole = if (asPublisher) { QBConferenceRole.PUBLISHER } else { QBConferenceRole.LISTENER } currentConferenceSession.joinDialog(userId, conferenceRole, object : ConferenceEntityCallback<ArrayList<Int>> { override fun onSuccess(publishers: ArrayList<Int>?) { } override fun onError(exception: WsException?) { } }) ``` </Tab> </Tabs> To subscribe to the active publisher, you should use the snippet below: <Tabs> <Tab title="Java"> ```Java theme={null} Set<Integer> publishersSet = currentConferenceSession.getActivePublishers(); for (Integer publisher : publishersSet) { currentConferenceSession.subscribeToPublisher(publisher); } ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val publishersSet = currentConferenceSession.activePublishers for (publisher in publishersSet) { currentConferenceSession.subscribeToPublisher(publisher) } ``` </Tab> </Tabs> <Note> You should subscribe to publishers only when the session state becomes connected. Use `onStateChanged()` callback method to track session states. </Note> If you are subscribing as a listener, then you can subscribe to publishers right after the successful `joinDialog()`. To unsubscribe from the publisher, use the `unSubscribeFromPublisher()` method. <Tabs> <Tab title="Java"> ```Java theme={null} currentConferenceSession.unsubscribeFromPublisher(publisher); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} currentConferenceSession.unsubscribeFromPublisher(publisher) ``` </Tab> </Tabs> ## Mute local audio Mute the audio by calling `getLocalAudioTrack().setEnabled()` method. Using this method, we can tell SDK to send/not send audio data from a local peer in the specified WebRTC session. <Tabs> <Tab title="Java"> ```Java theme={null} currentConferenceSession.getMediaStreamManager().getLocalAudioTrack().setEnabled(isAudioEnabled); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} currentConferenceSession.mediaStreamManager.localAudioTrack.setEnabled(isAudioEnabled) ``` </Tab> </Tabs> ## Mute remote audio You can always get remote audio tracks for a specific user ID in the call using the above-specified `QBRTCSession` methods (assuming that they are existent). You can also mute remote media tracks on your side by changing the value of the enabled property for a specific remote media track. <Tabs> <Tab title="Java"> ```Java theme={null} boolean isAudioEnabled = false; currentConferenceSession.getMediaStreamManager().getAudioTrack(opponentId).setEnabled(isAudioEnabled); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val isAudioEnabled = false currentConferenceSession.mediaStreamManager.getAudioTrack(opponentId).setEnabled(isAudioEnabled) ``` </Tab> </Tabs> ## Disable local video Turn off the video by calling `getLocalVideoTrack().setEnabled()`. Using this method, we can tell SDK not to send video data from a local peer in the specified session. <Tabs> <Tab title="Java"> ```Java theme={null} currentConferenceSession.getMediaStreamManager().getLocalVideoTrack().setEnabled(isVideoEnabled); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} currentConferenceSession.mediaStreamManager.localVideoTrack.setEnabled(isVideoEnabled) ``` </Tab> </Tabs> ## Disable remote video Turn off the video by calling `getVideoTrack(opponentID).setEnabled()`. Using this method, we can tell SDK not to send video data from a remote peer in the specified session. <Tabs> <Tab title="Java"> ```Java theme={null} boolean isVideoEnabled = false; currentConferenceSession.getMediaStreamManager().getVideoTrack(opponentId).setEnabled(isVideoEnabled); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} val isVideoEnabled = false currentConferenceSession.mediaStreamManager.getVideoTrack(opponentId).setEnabled(isVideoEnabled) ``` </Tab> </Tabs> ## Leave video room To leave the current joined video room, use the `leave()` method. <Tabs> <Tab title="Java"> ```Java theme={null} currentConferenceSession.leave(); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} currentConferenceSession.leave() ``` </Tab> </Tabs> ## Camera resolution The camera resolution is a video stream encoding parameter. It's possible to set the custom video resolution to provide guarantees for the predictable behavior of the video stream. <Tabs> <Tab title="Java"> ```Java theme={null} int videoWidth = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.width; int videoHeight = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.height; // VGA Resolution //videoWidth = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.width; //videoHeight = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.height; // HD Resolution //videoWidth = QBRTCMediaConfig.VideoQuality.HD_VIDEO.width; //videoHeight = QBRTCMediaConfig.VideoQuality.HD_VIDEO.height; // custom Resolution (for example FullHD) //videoWidth = 1920; //videoHeight = 1080; QBRTCMediaConfig.setVideoWidth(videoWidth); QBRTCMediaConfig.setVideoHeight(videoHeight); ``` </Tab> <Tab title="Kotlin"> ```Kotlin theme={null} var videoWidth = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.width var videoHeight = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.height // VGA Resolution //videoWidth = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.width //videoHeight = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.height // HD Resolution //videoWidth = QBRTCMediaConfig.VideoQuality.HD_VIDEO.width //videoHeight = QBRTCMediaConfig.VideoQuality.HD_VIDEO.height // Custom Resolution (for example FullHD) //videoWidth = 1920 //videoHeight = 1080 QBRTCMediaConfig.setVideoWidth(videoWidth) QBRTCMediaConfig.setVideoHeight(videoHeight) ``` </Tab> </Tabs> # Address Book Source: https://docs.quickblox.com/sdks/flutter-address-book Learn how to store and sync the phone contact list with QuickBlox. This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://help.quickblox.com/). # Authentication Source: https://docs.quickblox.com/sdks/flutter-authentication Use this page to learn how to authenticate your users with QuickBlox. Every user needs to authenticate with QuickBlox before using any QuickBlox functionality. When someone connects with an application using QuickBlox, the application needs to obtain a session token which provides temporary secure access to QuickBlox APIs. A session token is an opaque string that identifies a user and an application. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. ## Session token rights There are different types of session tokens to support different use cases. | Application session token | Description | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Application session token | This kind of access token is needed to read the app data. Has only READ access to resources. The expiration time after the last REST API request is 2 hours. | | User session token | The user token is the most commonly used type of token. This kind of access token is needed any time the app calls an API to read, modify or write a specific user's data on their behalf. Has READ/WRITE access to resources. The expiration time after the last REST API request is 2 hours. | ## Get session You can check whether you have a session or not. ```Dart Dart theme={null} try { QBSession? session = await QB.auth.getSession(); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` ## Sign up user Before you log in the user, you must create the user on QuickBlox. Recommendations are below: 1. **For POCs/MVPs:** Create the user using the [QuickBlox Dashboard](https://admin.quickblox.com/) or in client app with application session token. 2. **For production apps:** Use the [QuickBlox Create User API](https://docs.quickblox.com/reference/create-user) with [API key](/docs/application#create-api-key) on your backend to create the user when your user signs up in your app. <Warning> **Security** It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. </Warning> You can create a user with application session token in client app by calling `createUser()` method. ```Bash Bash theme={null} String login = "johnsmith"; String password = "superPassword"; String fullName = "John Smith"; try { QBUser? user = await QB.users.createUser(login, password, fullName: fullName); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Log in user If you have an application session, you can upgrade it to a user session by calling `login()` method. ```Dart Dart theme={null} String login = "johnsmith"; String password = "superPassword"; try { QBLoginResult result = await QB.auth.login('login', 'password'); QBUser? qbUser = result.qbUser; QBSession? qbSession = result.qbSession; } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` ## Log out user If you have a user session, you can downgrade it to an application session by calling `logout()` method. ```Dart Dart theme={null} try { await QB.auth.logout(); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` ## Session expiration The expiration time for a session token is 2 hours. If you will perform a query with an expired token, you will receive an error: `Required session does not exist`. In this case, you have to recreate the session token. In Flutter SDK since version 0.9.0, we have a listener to find if the session token has expired. ```Dart Dart theme={null} await QB.auth.subscribeAuthEvent(QBAuthEvents.SESSION_EXPIRED, (data) { // handle session expired event }); ``` ## Set existing session Typically, a session token is stored in SDK after successful login and used for every subsequent API call. However, you may want to obtain and store the session on your server for better security. In this case, you can set [application or user token](https://docs.quickblox.com/reference/authentication#application-vs-user-session-token) into SDK using the `startSessionWithToken()` method, also before calling the method to get notified that the session has expired you need to add [session expiration](/sdks/flutter-authentication#session-expiration) listener. Don't forget to [log in user](/sdks/flutter-authentication#log-in-user) if you pass the application token. ```Dart Dart theme={null} try { String sessionToken = "8b75a6c7191285499d890a81df4ee7fe49bc732a"; QBSession? session = await QB.auth.startSessionWithToken(sessionToken); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` <Warning> If you have a version lower than 0.9.0, you can set the existing session token into SDK using the setSession() method. </Warning> ```Dart Dart theme={null} try { QBSession? session = QBSession(); session!.applicationId = 76730; session!.userId = 567527986; session!.expirationDate = "2025-01-23T01:23:45.678+09:00"; session!.token = "8b75a6c7191285499d890a81df4ee7fe49bc732a"; QBSession? sessionResult = await QB.auth.setSession(session); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` Set the following fields of the `qbSession`. | Field | Required | Description | | -------------- | -------- | ------------------------------ | | applicationId | yes | Application ID. | | userId | yes | User ID. | | expirationDate | yes | Session token expiration date. | | token | yes | QuickBlox session token. | # Connection Source: https://docs.quickblox.com/sdks/flutter-chat-connection Learn how to connect to the chat server and set connection settings. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Connect to Chat server To connect to Chat server use the code snippet below. ```Dart Dart theme={null} String login = "chrispeterson"; String password = "superPassword"; try { await QB.chat.connect(login, password); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Subscribe to connection state Subscribe to the connection state changes using the following code snippet. ```Dart Dart theme={null} //Chat Connections //QBChatEvents.CONNECTED //QBChatEvents.CONNECTION_CLOSED //QBChatEvents.RECONNECTION_FAILED //QBChatEvents.RECONNECTION_SUCCESSFUL String event = QBChatEvents.CONNECTED; try { connectedSubscription = await QB.chat.subscribeChatEvent(event, (data) { } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Check if connected to Chat server Check the connection state using the following code snippet. ```Dart Dart theme={null} try { bool? connected = await QB.chat.isConnected(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Disconnect from Chat server Disconnect from the Chat server using the snippet below. ```Dart Dart theme={null} try { await QB.chat.disconnect(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Enable auto-reconnect to Chat The SDK reconnects automatically when the connection to the Chat server is lost. There is a way to disable it and then manage it manually. ```Dart Dart theme={null} try { await QB.settings.enableAutoReconnect(true); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Manage Chat connections To provide a seamless chat experience, our SDK manages connections to the Chat server at an application-wide level. Thus, to handle [offline messages](/sdks/flutter-chat-offline-messaging) correctly, use the `disconnect()` method when an app goes to the background and the `connect()` method when an app goes to the foreground. As a result, the SDK will disconnect/connect the current user from the Chat server when the app goes to the background/foreground mode. ```Dart Dart theme={null} class _SomeScreenState extends State<SomeScreen> with WidgetsBindingObserver { //... @override initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.resumed: try { await QB.chat.connect(userId, userPassword); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } break; case AppLifecycleState.paused: print("app in paused"); try { await QB.chat.disconnect(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } break; } } } //... ``` | Argument | Required | Description | | ------------ | -------- | ------------------------- | | userId | yes | The ID of a user. | | userPassword | yes | The password of the user. | # Contact List Source: https://docs.quickblox.com/sdks/flutter-chat-contact-list Learn how to create, update, manage, and add users to your contact list. This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://help.quickblox.com/). # Dialogs Source: https://docs.quickblox.com/sdks/flutter-chat-dialogs Learn how to create and manage dialogs. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/flutter-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Dialog types All chats between users are organized in dialogs. There are 3 types of dialogs: * **private dialog** - a dialog between 2 users. * **group dialog** - a dialog between the specified list of users. * **public dialog** - an open dialog. Any user from your app can be joined to it. You need to create a new dialog and then use it to chat with other users. You also can obtain a list of your existing dialogs. ## Create dialog To create a **private** dialog, you need to set the ID of the opponent you want to create a chat with. ```Dart Dart theme={null} List<int> occupantsIds = [89987878, 98987887]; String dialogName = "someone chat"; int dialogType = QBChatDialogTypes.CHAT; try { QBDialog? createdDialog = await QB.chat.createDialog(occupantsIds, dialogName, dialogType: dialogType); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` To create a **group** dialog for a predefined number of occupants, you need to set the IDs of opponents you want to create a chat with. ```Dart Dart theme={null} List<int> occupantIds = [234234324, 3243243]; String dialogName = "Group Chat"; String dialogPhoto = "some photo url"; int dialogType = QBChatDialogTypes.GROUP_CHAT; try { QBDialog? createdDialog = await QB.chat.createDialog(occupantIds, dialogName, dialogType: dialogType, dialogPhoto: dialogPhoto); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` It's possible to create a **public** dialog, so any user from your application can be joined to it. There is no list of occupants. This dialog is open for everybody. ```Dart Dart theme={null} List<int> occupantIds = [234234324, 3243243]; int dialogType = QBChatDialogTypes.PUBLIC_CHAT; String dialogName = "Public Chat"; try { QBDialog? createdDialog = await QB.chat.createDialog(occupantIds, dialogName, dialogType: dialogType); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` The `createDialog()` method accepts the following arguments: | Argument | Required | Description | | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | occupantIds | yes | A list of opponents IDs.- If the occupantsIds array is empty and type is not provided, the **public** dialog is created.- If the occupantsIds has a single user and type is not provided, the **private** dialog is created.- If the occupantsIds has more then one userId and type is not provided, a **group** dialog is created. | | dialogName | yes | The name of the dialog. Required only for **group** and **public** dialog types. Not needed for **private** dialog. | | dialogType | no | A type of the dialog. Possible values: `QBChatDialogTypes.CHAT`, `QBChatDialogTypes.GROUP\_CHAT` or `QBChatDialogTypes.PUBLIC\_CHAT`. By default, the **public** dialog is created. | | dialogPhoto | no | A url of the image. Can be a link to a file in Content module, Custom Objects module or just a web link.**Must** be a String. | | isJoinRequired | no | Defines whether participants must join before sending or receiving real-time messages. Applies **only to group dialogs**. | ## Create group dialog with join required <Warning> Available since **QuickBlox Flutter SDK v0.19.0**. Prior to **server version 2.34.0**, all group dialogs required joining. Starting from **server version 2.34.0**, new applications do not require joining, while existing applications retain the previous behavior. You can change the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). If `isJoinRequired` is explicitly set when creating a dialog, the provided value takes priority over the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). Most applications do not need this feature. The default behavior where participants can send and receive real-time messages without joining is recommended for most use cases. </Warning> When creating a **group** dialog, you can set the `isJoinRequired` parameter to `true` to require participants to explicitly join the dialog before they can send or receive real-time messages. This is only needed when you want to restrict real-time messaging in specific dialogs until participants explicitly join. By default, `isJoinRequired` is `false` and participants can message without joining. This parameter applies **only to group dialogs**. You can change the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). ```Dart Dart theme={null} List<int> occupantIds = [234234324, 3243243]; String dialogName = "Group Chat"; int dialogType = QBChatDialogTypes.GROUP_CHAT; try { QBDialog? createdDialog = await QB.chat.createDialog( dialogType, occupantsIds: occupantIds, dialogName: dialogName, isJoinRequired: true, ); bool isJoinRequired = await QB.chat.isJoinRequired(createdDialog!.id!); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` ## Check if join required for group dialog <Info> The `isJoinRequired()` method is available starting from **QuickBlox Flutter SDK v0.19.0**. See [Create group dialog with join required](/sdks/flutter-chat-dialogs#create-group-dialog-with-join-required) for details. </Info> You can get the `isJoinRequired` value for any group dialog: ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { bool isJoinRequired = await QB.chat.isJoinRequired(dialogId); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` ## Join group dialog <Info> Starting from **QuickBlox Flutter SDK v0.19.0**, joining a group dialog is required only when `isJoinRequired` is set to `true` for a dialog. See [Create group dialog with join required](/sdks/flutter-chat-dialogs#create-group-dialog-with-join-required) for details. </Info> If `isJoinRequired` is set to `true` for a group dialog, you need to join it by calling the `joinDialog()` method before you can send or receive real-time messages. See [this section](/sdks/flutter-chat-messaging#send-text-message) to learn how to send/receive real-time messages. You must join the dialog after every new connection or reconnection. If the connection is lost and then restored, whether manually or automatically, you need to call `joinDialog()` again for each dialog where `isJoinRequired` is `true`. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { await QB.chat.joinDialog(dialogId); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` <Note> You can join a group dialog **only** if your user ID is present in the `occupantIDs` array in the dialog model. Your user ID is added to the `occupantIDs` array if you create a dialog **or** you are added to the dialog by another user. See [this section](/sdks/flutter-chat-dialogs#add-occupants) to learn how to add occupants to the group dialog. </Note> To check if you have already joined the dialog, call the `isJoinedDialog()` method: ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { bool isJoinedDialog = await QB.chat.isJoinedDialog(dialogId); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` ## Join public dialog Before you start chatting in a **public** dialog, you must join it by calling the `joinDialog()` method. Unlike group dialogs, joining a public dialog is always required. If you've successfully joined the dialog, you can send/receive real-time messages. See [this section](/sdks/flutter-chat-messaging#send-text-message) to learn how to send/receive real-time messages. You must join the dialog after every new connection or reconnection. If the connection is lost and then restored, whether manually or automatically, you need to call `joinDialog()` again. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { await QB.chat.joinDialog(dialogId); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` To check if you have already joined the dialog, call the `isJoinedDialog()` method: ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { bool isJoinedDialog = await QB.chat.isJoinedDialog(dialogId); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` ## Leave group dialog You can leave the **group** dialog by calling the `leaveDialog()` method. After leaving, you will stop receiving real-time messages from this dialog. You need to join the dialog again to resume receiving real-time messages. <Info> Starting from **QuickBlox Flutter SDK v0.19.0**, leaving a group dialog is only needed when `isJoinRequired` is set to `true`. If `isJoinRequired` is `false`, you do not need to call `leaveDialog()`. </Info> ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { await QB.chat.leaveDialog(dialogId); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` <Note> When you leave a group dialog, your user ID is still present in the `occupantIDs` array in the dialog model. The dialog will still appear in the list of dialogs and you will still have access to the chat history. To remove yourself from the group dialog, use the `updateDialog()` method. See [this section](/sdks/flutter-chat-dialogs#remove-occupants) to learn how to remove occupants from the group dialog. </Note> ## Leave public dialog You can leave the **public** dialog by calling the `leaveDialog()` method. After leaving, you will stop receiving real-time messages from this dialog. You need to join the dialog again to resume receiving real-time messages. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { await QB.chat.leaveDialog(dialogId); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` ## Retrieve list of dialogs It's common to request all your dialogs on every app login. The request below will return **private**, **group**, and **public** dialogs containing `test value` in their names, sorted in ascending order by the `QBChatDialogFilterFields.LAST_MESSAGE_DATE_SENT` field, and limited to 100 dialogs on the page with 25 dialogs skipped at the beginning. ```Dart Dart theme={null} QBSort sort = QBSort(); sort.field = QBChatDialogSorts.LAST_MESSAGE_DATE_SENT; sort.ascending = true; QBFilter filter = QBFilter(); filter.field = QBChatDialogFilterFields.NAME; filter.operator = QBChatDialogFilterOperators.CTN; filter.value = "test value"; int limit = 100; int skip = 25; try { List<QBDialog?> dialogs = await QB.chat.getDialogs(sort: sort, filter: filter, limit: limit, skip: skip); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | ------------------------------------------------------------------------------------------- | | sort | no | Specifies sorting criteria for the field. | | filter | no | Specifies filtering criteria for the field. | | limit | no | Limit search results to N records. Useful for pagination. Default value: **100**. | | skip | no | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | If you want to retrieve only dialogs updated after some specific date time and order the search results, you can apply operators. This is useful if you cache dialogs somehow and do not want to obtain the whole list of your dialogs on every app start. Thus, you can apply [search](/sdks/flutter-chat-dialogs#search-operators) and [sort](/sdks/flutter-chat-dialogs#sort-operators) operators to list dialogs on the page so that it is easier to view specific dialogs. ### Search operators You can use search operators to get more specific search results. The request below will return all dialogs containing the `flutter_chat` in their names. ```Dart Dart theme={null} try { QBFilter filter = QBFilter(); filter.field = QBChatDialogFilterFields.NAME; filter.operator = QBChatDialogFilterOperators.CTN; filter.value = "flutter_chat"; List<QBDialog?> dialogs = await QB.chat.getDialogs(filter: filter); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | --------------------------------------------------- | ---------------------------------------------------- | | lt | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** operator. | | lte | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** or **Equal** to operator. | | gt | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** operator. | | gte | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** or **Equal** to operator. | | ne | number, string, date | \_id, name, last\_message\_date\_sent | **Not Equal** to operator. | | in | number, string, date | type, last\_message\_date\_sent, name | **IN** array operator. | | nin | number, string, date | last\_message\_date\_sent | **IN** array operator. | | all | number | occupants\_ids | **ALL** are contained in array. | | ctn | string | name | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return a list of dialogs sorted by the `QBChatDialogSorts.LAST_MESSAGE_DATE_SENT` field in descending order. ```Dart Dart theme={null} try { QBSort sort = QBSort(); sort.field = QBChatDialogSorts.LAST_MESSAGE_DATE_SENT; sort.ascending = false; List<QBDialog?> dialogs = await QB.chat.getDialogs(sort: sort); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` Here are the sort options that you can use to order the search results. | Sort options | Applicable to types | Applicable to fields | Description | | ------------ | ------------------- | ------------------------------------------------ | ------------------------------------------------------------------- | | ascending | All types | id, created\_at, name, last\_message\_date\_sent | Sort results in ascending order by setting the ascending as true. | | descending | All types | id, created\_at, name, last\_message\_date\_sent | Sort results in descending order by setting the ascending as false. | ## Update dialog You can update the information for a **private**, **group**, and **public** dialog. ```Dart Dart theme={null} String dialogId = "adfpo443ldfJLsdjdsf324lkjer"; String dialogName = "Some Chat"; String dialogPhoto = "some photo url"; try { QBDialog? updatedDialog = await QB.chat.updateDialog(dialogId, dialogName: dialogName, dialogPhoto: dialogPhoto); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | ----------- | -------- | --------------------------------------- | | dialogId | yes | The ID of the dialog. | | dialogName | no | A name of the dialog. | | dialogPhoto | no | A url of the image. Should be a String. | Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | -------------------- | ------------- | ------------ | -------------- | | Update a dialog name | Owner | Owner | ✗ | | Update a photo | Owner | Owner | ✗ | ## Add occupants Set the `addUsers` argument to add occupants to the dialog. As a result, the ID of the opponent will be added to the `occupantIds` array. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; List<int> addUsers = [234234111, 32432777]; try { QBDialog? updatedDialog = await QB.chat.updateDialog(dialogId, addUsers: addUsers); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | --------------- | ------------- | -------------- | -------------- | | Add other users | ✗ | Owner,Occupant | ✗ | ## Remove occupants Set the `removeUsers` argument to remove occupants from the dialog. As a result, the ID of the opponent will be removed from the `occupantIds` array. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; List<int> removeUsers = [234234888, 3243999]; try { QBDialog? updatedDialog = await QB.chat.updateDialog(dialogId, removeUsers: removeUsers); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` | Capabilities | Public dialog | Group dialog | Private dialog | | ------------------ | ------------- | -------------- | -------------- | | Remove other users | ✗ | Owner | ✗ | | Remove yourself | ✗ | Owner,Occupant | ✗ | ## Delete dialog Delete a dialog for all users using the request below. When deleting a **group** dialog, all user IDs will be removed from the `occupantIds` array in the dialog model. You can also delete multiple dialogs in a single request. To delete a dialog for yourself, just leave the dialog. See [Leave group dialog](/sdks/flutter-chat-dialogs#leave-group-dialog) and [Leave public dialog](/sdks/flutter-chat-dialogs#leave-public-dialog) for more information. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { await QB.chat.deleteDialog(dialogId); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } ``` Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public | Group | Private | | ----------------------------- | ------ | ----- | ------- | | Delete a dialog for all users | Owner | Owner | Owner | ## Resources A sequence of steps a user takes to start a dialog by moving through the application lifecycle. <img alt="flutter-starting-dialog.jpg" /> # Messaging Source: https://docs.quickblox.com/sdks/flutter-chat-messaging Learn how to send and receive messages, mark messages as delivered or read, etc. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/flutter-chat-connection) page to learn how to do it. 5. Create a dialog. See [Dialogs](/sdks/flutter-chat-dialogs) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Subscribe message events Subscribe message events using the `subscribeChatEvent()` method. As a result, the app will receive events associated with receiving a message, delivery receipts, and read receipts. ```Dart Dart theme={null} //Chat events //QBChatEvents.RECEIVED_NEW_MESSAGE //QBChatEvents.RECEIVED_SYSTEM_MESSAGE //QBChatEvents.MESSAGE_DELIVERED //QBChatEvents.MESSAGE_READ StreamSubscription? _someSubscription; ... @override void dispose() { if(_someSubscription != null) { _someSubscription!.cancel(); _someSubscription = null; } } ... String event = QBChatEvents.RECEIVED_NEW_MESSAGE; try { _someSubscription = await QB.chat.subscribeChatEvent(event, (data) { Map<dynamic, dynamic> map = Map<dynamic, dynamic>.from(data); Map<dynamic, dynamic> payload = Map<dynamic, dynamic>.from(map["payload"]); String? messageId = payload["id"]; } }); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Send text message To send a message to a **private** dialog, use the code snippet below. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; String body = "test body"; List<QBAttachment>? attachments = []; Map<String, String>? properties = Map(); bool markable = false; String dateSent = "2000-023T01:23:45.678+09:00"; bool saveToHistory = true; try { await QB.chat.sendMessage(dialogId, body: body, attachments: attachments, properties: properties, markable: markable, dateSent: dateSent, saveToHistory: saveToHistory); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | dialogId | yes | The ID of a dialog. | | messageBody | no | A message text. | | attachments | no | A list of attachments. | | properties | no | A list of properties. | | markable | no | A boolean parameter. A markable status of a message. | | dateSent | no | The date the message was sent. | | saveToHistory | no | A boolean parameter. Specifies if the message will be saved on the server. Set the saveToHistory as true to save the message on the server. | <Note> For **public** dialogs, you must join before sending or receiving real-time messages. For **group** dialogs, joining is required only when `isJoinRequired` is set to `true`. See [Join group dialog](/sdks/flutter-chat-dialogs#join-group-dialog) and [Join public dialog](/sdks/flutter-chat-dialogs#join-public-dialog) to learn how to join dialogs. </Note> To send messages to a **group/public dialog**, use the following code snippet below. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; String body = "test body"; bool saveToHistory = true; try { await QB.chat.sendMessage(dialogId, body: body, saveToHistory: saveToHistory); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` Use the same code snippet to send/receive messages for **private**, **group**, and **public** dialog. ## Send message with attachment Chat attachments are supported by the [content API](/sdks/flutter-content). In order to send a chat attachment, you need to upload the file to QuickBlox cloud storage and obtain a link to the file (file UID). Then you need to include this UID into chat message and send it. ```Dart Dart theme={null} String url = "https://picutres.com/9384erw343.jpg"; bool public = false; try { QBFile? file = await QB.content.upload(url, public: public); if(file != null) { int id = file!.id; String contentType = file.contentType; QBAttachment attachment = QBAttachment(); attachment.id = id.toString(); attachment.contentType = contentType; //Required parameter attachment.type = "PHOTO"; List<QBAttachment> attachmentsList = []; attachmentsList.add(attachment); QBMessage message = QBMessage(); message.attachments = attachmentsList; // Send a message logic } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` The flow on the receiver's side is the following: when you receive a message, you need to get the file URL to download the file from the cloud storage. ```Dart Dart theme={null} String uid = "0we8fjlkj34432n2lfkdj"; try { String? url = await QB.content.getPrivateURL(uid); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Send message with extra data You have the option to extend the message with additional fields. Specify one or more **key-value** items to the message. Using these items, you can implement the ability for a user to send self-location information to another user or notification messages signifying that a user has left a group, etc. ```Dart Dart theme={null} String dialogId = "dsfs9344349hjkdsda9877932j2"; String body: 'How are you today!'; bool saveToHistory = true; Map<String, String> properties = Map(); properties["customParam1"] = "book"; properties["customParam2"] = "21"; try { await QB.chat.sendMessage(dialogId, body: body, saveToHistory: saveToHistory, properties: properties); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | dialogId | yes | The ID of a dialog. | | body | no | A message text. | | saveToHistory | no | A boolean parameter. Specifies if the message will be saved on the server. Set the saveToHistory as true to save the message on the server. | | properties | no | Extra data. Specify any key-value pairs. In each pair, the key and value are both string values. | ## Retrieve message history Every dialog stores its chat history that you can retrieve using the `getDialogMessages()` method. The request below will return messages for a specific dialog containing `test value` in its name, sorted by the `QBChatDialogSorts.LAST_MESSAGE_DATE_SENT` field in ascending order, limited to 50 messages on the page. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; QBSort sort = QBSort(); sort.field = QBChatMessageSorts.DATE_SENT; sort.ascending = false; QBFilter filter = QBFilter(); filter.field = QBChatMessageFilterFields.ID; filter.value = messageID; filter.operator = QBChatMessageFilterOperators.IN; int limit = 100; int skip = 50; bool markAsRead = true; try { List<QBMessage?> messages = await QB.chat.getDialogMessages(dialogId, sort: sort, filter: filter, limit: limit, skip: skip, markAsRead: markAsRead); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | dialogId | yes | The ID of the dialog. | | sort | no | Specifies sorting criteria for the field. | | filter | no | Specifies filtering criteria for the field. | | limit | no | Limit search results to N records. Useful for pagination. Default value: **100**. | | skip | no | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | markAsRead | no | A boolean parameter. If you want to mark all retrieved chat messages as a read, set the markAsRead parameter as true. If you decide not to mark chat messages as read, just set markAsRead parameter as false or omit the parameter. | If you want to retrieve only messages updated after some specific date time and order the search results, you can apply operators. This is useful if you cache messages somehow and do not want to obtain the whole list of messages on every app start. Thus, you can apply [search](/sdks/flutter-chat-messaging#search-operators) and [sort](/sdks/flutter-chat-messaging#sort-operators) operators to list messages on the page so that it is easier to view specific messages. The operators are set as key-value parameters in the `extendedRequest` dictionary. ### Search operators You can use search operators to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | ---------------------------------------------------- | ---------------------------------------------------- | | lt | Number, String, Date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** operator. | | lte | Number, String, Date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** or **Equal** to operator. | | gt | Number, String, Date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** operator. | | gte | Number, String, Date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** or **Equal** to operator. | | ne | Number, String, Date | \_id, message, date\_sent, sender\_id, recipient\_id | **Not Equal** to operator. | | in | Number, String, Date | date\_sent, sender\_id, recipient\_id | **IN** array operator. | | nin | Number, String, Date | date\_sent, sender\_id, recipient\_id | Not **IN** array operator. | | or | Number, String, Date | date\_sent, sender\_id, recipient\_id | All records that contain a value 1 **or** value 2. | | ctn | String | message | All records that **contain** a particular substring. | ### Sort operators Here are the sort options that you can use to order the search results. | Sort options | Applicable to types | Description | | ------------ | ------------------- | ----------------------------------------------------------------------- | | ascending | All types | Sort results in the ascending order by setting the ascending as true. | | descending | All types | Sort results in the descending order by setting the ascending as false. | ## Check if a message is sent The message is considered as **sent** if it has been delivered to the server. To get to know that a message has been delivered to the server, make sure to enable a stream management before connecting to the Chat server. See [this section](/sdks/flutter-setup#stream-management) to learn how to enable the stream management. Thus, you send a message to the server and if no error is returned, it is considered as **sent** (by default). There is no field for a sent status in the message model. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; String body = "hello"; try { await QB.chat.sendMessage(dialogId, body: body); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` <Warning> You should enable Stream Management before you do the `login()` because the Stream Management is initialized while Chat login is performed. The Stream Management defines an extension for active management of a stream between a client and server, including features for stanza acknowledgments. </Warning> ## Mark message as delivered As a sender, you may want to be informed that a message has been successfully delivered to the recipient. The mark-as-delivered functionality allows to notify the sender about message delivery. To track the event when the message has been delivered to the user, you need to subscribe to this event. As a result, when a user receives a message, the SDK receives the `QBChatEvents.MESSAGE_DELIVERED` event. ```Dart Dart theme={null} StreamSubscription? _deliveredMessageSubscription; ... @override void dispose() { if(_deliveredMessageSubscription != null) { _deliveredMessageSubscription!.cancel(); _deliveredMessageSubscription = null; } } ... try { _deliveredMessageSubscription = await QB.chat.subscribeChatEvent(QBChatEvents.MESSAGE_DELIVERED, (data) { LinkedHashMap<dynamic, dynamic> messageStatusHashMap = data; Map<dynamic, dynamic> messageStatusMap = Map<dynamic, dynamic>.from(messageStatusHashMap); Map<dynamic, dynamic> payloadMap = Map<String,Object>.from(messageStatusHashMap["payload"]); String messageId = payloadMap["messageId"]; String userId = payloadMap["userId"]; String statusType = messageStatusMap["type"]; } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` Use the `markMessageDelivered()` method to mark a message as delivered. As a result, the server will notify a sender about the delivery receipt. ```Dart Dart theme={null} QBMessage message = new QBMessage(); message.dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; message.id = "dslsldf8873lkl&&KjJKJ8fe"; message.senderId = 89987878; try { await QB.chat.markMessageDelivered(message); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` A message can be marked as delivered automatically by the server once a message is successfully delivered to the recipient. Set the `markable` as `true` using the `sendMessage()` method if you want, as a sender, to receive message delivery receipts from other recipients. Thus, the `markable` parameter enables the sender to request the delivery receipt. It also enables the recipient to confirm the message delivery. However, if `markable` is `false` or omitted, then you can notify a sender about the delivery receipt using the `markMessageDelivered()` method. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; String body = "test body"; List<QBAttachment>? attachments = []; Map<String, String>? properties = Map(); bool markable = false; String dateSent = "2000-023T01:23:45.678+09:00"; bool saveToHistory = true; try { await QB.chat.sendMessage(dialogId, body: body, attachments: attachments, properties: properties, markable: markable, dateSent: dateSent, saveToHistory: saveToHistory); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` <Note> Make sure to understand, that marking-as-delivered operation just confirms the fact of message delivery. The message acquires the **delivered** status when the `QBChatEvents.MESSAGE_DELIVERED` event is received. When a message is marked as delivered, the IDs of users who have received the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who received the message using the `getDialogMessages()` method. See [this section](/sdks/flutter-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Note> ## Mark message as read As a sender, you may want to be informed that a message has been read by the recipient. The mark-as-read functionality allows to notify the sender that a message has been read. To track the event when the message has been read by the user, you need to subscribe to this event using the code snippet below. As a result, when a user reads a message, the SDK receives the `QBChatEvents.MESSAGE_READ` event. ```Dart Dart theme={null} StreamSubscription? _readMessageSubscription; ... @override void dispose() { if(_readMessageSubscription != null) { _readMessageSubscription!.cancel(); _readMessageSubscription = null; } } ... try { _readMessageSubscription = await QB.chat.subscribeChatEvent(QBChatEvents.MESSAGE_READ, (data) { LinkedHashMap<dynamic, dynamic> messageStatusHashMap = data; Map<dynamic, dynamic> messageStatusMap = Map<dynamic, dynamic>.from(messageStatusHashMap); Map<dynamic, dynamic> payloadMap = Map<dynamic, dynamic>.from(messageStatusHashMap["payload"]); String messageId = payloadMap["messageId"]; String userId = payloadMap["userId"]; String statusType = messageStatusMap["type"]; } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` Use the `markMessageRead()` method to mark a message as read. As a result, the server will notify a sender about the read receipt. ```Dart Dart theme={null} QBMessage message = new QBMessage(); message.dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; message.id = "dslsldf8873lkl&&KjJKJ8fe"; message.senderId = 89987878; try { await QB.chat.markMessageRead(message); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` <Note> When a message is marked as read, the IDs of users who have read the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who read the message using the `getDialogMessages()` method. See [this section](/sdks/flutter-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Note> ## Send typing indicators You may want, as a sender, to let the recipient know that you are typing the message or have stopped typing the message. Use typing indicators as a form of chat-specific presence. Typing indicators allow to indicate if users are typing messages in a dialog at the moment. There are the following **typing** notifications supported. * **typing**. The user is composing a message. The user is actively interacting with a message input interface specific to this chat session (for example, by typing in the input area of a chat window). * **stopped**. The user had been composing but now has stopped. The user has been composing but has not interacted with the message input interface for a short period of time (for example, 30 seconds). To track the event when the sender is typing the message, you need to subscribe to this event. As a result, when a sender is typing a message, the SDK receives the `QBChatEvents.USER_IS_TYPING` event. ```Dart Dart theme={null} StreamSubscription? _userTypingSubscription; ... @override void dispose() { if(_userTypingSubscription != null){ _userTypingSubscription!.cancel(); _userTypingSubscription = null; } } ... try { _userTypingSubscription = await QB.chat.subscribeChatEvent(QBChatEvents.USER_IS_TYPING, (data) { Map<dynamic, dynamic> map = Map<dynamic, dynamic>.from(data); Map<dynamic, dynamic> payload = Map<dynamic, dynamic>.from(map["payload"]); int userId = payload["userId"]; } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` To track the event when the sender has stopped typing, you need to subscribe to this event. Thus, when a sender has stopped typing a message, the SDK receives the `QBChatEvents.USER_STOPPED_TYPING` event. ```Dart Dart theme={null} StreamSubscription? _userStopTypingSubscription; ... @override void dispose() { if(_userStopTypingSubscription != null){ _userStopTypingSubscription!.cancel(); _userStopTypingSubscription = null; } } ... try { _userStopTypingSubscription = await QB.chat.subscribeChatEvent(QBChatEvents.USER_STOPPED_TYPING, (data) { Map<dynamic, dynamic> map = Map<dynamic, dynamic>.from(data); Map<dynamic, dynamic> payload = Map<dynamic, dynamic>.from(map["payload"]); int userId = payload["userId"]; } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` To notify a recipient that a sender is typing the message, use the `sendIsTyping()` method. As a result, the server will notify a recipient about the event. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { await QB.chat.sendIsTyping(dialogId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` To notify a recipient that a sender had been composing a message but now has stopped, use the `sendStoppedTyping()` method. As a result, the server will notify a recipient about the event. ```Dart Dart theme={null} String dialogId = "fdb5f5a28388a64aba5b2f57570b13f827012bba"; try { await QB.chat.sendStoppedTyping(dialogId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Send system messages There is a way to send system messages to other users about some events. For example, a system message can be sent when a user has joined or left a group dialog. These messages work over a separate channel and are not be mixed up with regular chat messages. Thus, in order to receive these messages, you should subscribe to the `QBChatEvents.RECEIVED_SYSTEM_MESSAGE` event. See [this section](/sdks/flutter-chat-messaging#subscribe-message-events) to learn how to subscribe to message events. System messages are also not shown in the dialog history and, consequently, are not stored on the server. This means that these messages will be delivered **only** to online users. Send system messages using the `sendSystemMessage()` method. ```Dart Dart theme={null} int recipientId = 109364799; Map<String, String> properties = Map(); properties["someKey"] = "someValue"; try { await QB.chat.sendSystemMessage(recipientId, properties: properties); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | ----------- | -------- | ------------------------------------------------------------------------------------------------ | | recipientId | yes | ID of the recipient. | | properties | no | Extra data. Specify any key-value pairs. In each pair, the key and value are both string values. | ## Notification dialog To enable the notifications for the specific dialog, use the `updateNotificationDialog()`method. ```Dart Dart theme={null} String dialogId = "as3kj3LJfds"; bool enabled = true; try { await QB.chat.updateNotificationDialog(dialogId, enabled); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | -------------------------------------------------------- | | dialogId | yes | ID of the dialog. | | enabled | yes | The parameter for enable/disable notification for dialog | # Offline Messaging Source: https://docs.quickblox.com/sdks/flutter-chat-offline-messaging Learn how to receive push notifications when the app is not running. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/flutter-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Enable offline messaging Send notifications about new messages to the user when their app is not running by using offline messaging functionality. This functionality allows to customize automatic push notifications displayed on a user's device when they are offline. In other words, if your opponent is offline when you are writing a message, they automatically receive a push notification. <Tip> Make sure to subscribe your users to pushes using SDKs. Review [Push Notifications](/sdks/ios-push-notifications) section for more details. </Tip> <Warning> Only FCM data messages can be sent from the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages in your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notifications. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> Enable offline messaging from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Check the **Enable automatic push notifications for offline users** if you want to receive push notifications from the server automatically. <Frame> <img alt="enable offline messaging.png" /> </Frame> ## Configure offline messaging template You can configure an offline messaging template from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Choose the template in the **Templates** section. <Note> Available for Growth/HIPAA Cloud/Enterprise plan You can set the custom template in the **Offline messaging text** section using the following template variables: * %unread\_count% - total number of new messages the recipient has * %plural\[new message]% - words in brackets (20 characters max) will be pluralized based on unread messages count * %body\[x]% - quote x characters from message body * %recipient\_fullname% - full name of recipient * %sender\_fullname% - full name of sender * %recipient\_login% - login of recipient * %sender\_login% - login of sender * %dialog\_name% - name of chat dialog (will be replaced with "Private dialog" string for 1-1 messages) </Note> 1. Set **Badge counter** to include counter info into your push message. Useful to include an unread counter that is the number of unread messages a user has. 2. Set push notification sound in the **Sound** section (for iOS only). 3. Set the **Category (iOS)** field. Enter "actions" that should be presented in the app notification in various views (for iOS only). 4. Enable the **Content Available** option to indicate that new content is available (for iOS only). 5. Enable the **Mutable content** option to modify the content of newly delivered notifications before the user is alerted. Use a notification service app extension to be able to modify the content of the notification. If the option is enabled, the system passes the notification to the service app extension for the subsequent modification of the notification payload before the actual delivery (for iOS only). 6. Click the **Save** button. <Frame> <img alt="configure template.png" /> </Frame> <Warning> Currently, push notifications are supported in a mobile environment only. </Warning> # Privacy List Source: https://docs.quickblox.com/sdks/flutter-chat-privacy-list Learn how to create privacy lists and implement user-to-user blocks. This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://help.quickblox.com/). # User Presence Source: https://docs.quickblox.com/sdks/flutter-chat-user-presence Learn how to track user presence updates and check user status using ping. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/flutter-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Check if a user is online <Warning> This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://assist.quickblox.com/). </Warning> ## Ping user QuickBlox SDK can send application-level pings to a user. As a result, you can check if the user is connected to the Chat server. ```Dart Dart theme={null} int userId = 89987878; try { await QB.chat.pingUser(userId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | --------------- | | userId | yes | ID of the user. | ## Ping server QuickBlox SDK can send application-level pings to a server. As a result, you can check if there is a connection with the Chat server. ```Dart Dart theme={null} try { await QB.chat.pingServer(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` # Content Source: https://docs.quickblox.com/sdks/flutter-content Learn how to store and access files with QuickBlox file storage. The content module allows storing rich chat attachments, app content, and settings without having to republish them. Using a web interface you or your clients can control and make instant changes to the apps. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. ## Upload file Upload a file to the cloud using the following code snippet. ```Dart Dart theme={null} String url = "content://images/test.jpg"; bool public = false; try { QBFile? file = await QB.content.upload(url, public: public); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | url | yes | URL of a file. | | public | no | Boolean parameter. Specifies file visibility. If the file is public then it's possible to download it without a session token. Default: **false**. | The maximum size of the uploaded file depends on the membership plan. | Basic | Startup | Growth | HIPAA | Enterprise | | | --------------- | ------- | ------ | ----- | ---------- | --------------------------------------------------------------- | | File size limit | 10 Mb | 25 Mb | 50Mb | 50Mb | [Contact our sales team](https://quickblox.com/enterprise/#get) | ## Get file info Get information about a file by ID using the `getInfo()` method below. This method allows to load the file description model from the server, not the content of the file itself. ```Dart Dart theme={null} int fileId = 98239423; try { QBFile? file = await QB.content.getInfo(fileId); if (file != null) { int id = file!.id; String uid = file!.uid; } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | --------------- | | fileId | yes | ID of the file. | ## Get file URL There are two types of file URLs that can be obtained: private and public. * **Public URL** allows anyone to access the file, no authorization token is needed. * **Private URL** can be accessed only by QuickBlox user with a session token. ### Get public URL To receive a public URL, use the code snippet below. ```Dart Dart theme={null} String fileUid = "sdfldsj890dfLKJJerJdkn8"; try { String! url = await QB.content.getPublicURL(fileUid); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | ----------------------------------------------------------------------------------------------------------- | | fileUid | yes | File unique identifier. You can get the fileUid from the file recieved as a result of the getInfo() method. | ### Get private URL To get a private URL of the uploaded file, use the following code snippet. ```Dart Dart theme={null} String fileUid = "sdfldsj890dfLKJJerJdkn8"; try { String! url = await QB.content.getPrivateURL(fileUid); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | ----------------------------------------------------------------------------------------------------------- | | fileUid | yes | File unique identifier. You can get the fileUid from the file recieved as a result of the getInfo() method. | # Custom Objects Source: https://docs.quickblox.com/sdks/flutter-custom-objects Learn how to store and sync data with QuickBlox key-value storage. Custom Objects module provides flexibility to define any data structure (schema) you need, build one-to-many relations between schemas and control permissions for all operations made on data. Schema is defined in QuickBlox Dashboard. There are two key concepts in Custom Objects: * **Class** represents your schema and contains field names and types. * **Record** represents the data you put into your schema. **Class** and **Record** are similar to table and row in relational database. Every class in Custom Object module comes with five mandatory predefined fields: `_id`, `user_id`, `parent_id`, `created_at`, and `updated_at`. Allowed data types: **Integer** (or Array of Integer); **String** (or Array of String); **Float** (or Array of Float); **Boolean** (or Array of Boolean); **Location** (Array of \[, ]); **File**; **Date**. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. ## Create class To start using Custom Objects module, create a class: 1. Go to [QuickBlox Dashboard](https://admin.quickblox.com/signin). 2. Follow **Custom => Add => Add new class** direction. As a result, **Add new class** popup will appear. 3. Enter a class name, add any fields you want. <Frame> <img alt="flutter-custom-add-filed.png" /> </Frame> 1. Click **Create class button** to create a new class. <Frame> <img alt="flutter-custom-fields.png" /> </Frame> ## Create records The easiest way to create a new record from the [QuickBlox Dashboard](https://admin.quickblox.com/signin), do the following: 1. Follow **Custom => Current class => Your Class** direction. 2. Click **Add => Add record** button and **Add new record** popup will appear. 3. Fill in any fields you want. 4. Click **Add record button** and a new record will be added and shown in the table. To create a single object, use the code snippet below. ```Dart Dart theme={null} String className = "TestFlutterClass"; Map<String, Object> fieldsMap = Map(); fieldsMap['testString'] = "testField"; fieldsMap['testInteger'] = 123; fieldsMap['testBoolean'] = true; try { List<QBCustomObject?> customObjects = await QB.data.create(className: className, fields: fieldsMap); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ----------------------------------- | | className | no | Name of a custom object class. | | fields | no | Data fields (Map\<String, Object>). | To create a multiple objects, use the code snippet below. ```Dart Dart theme={null} Map<String, Object> fieldsMapOne = Map(); fieldsMapOne['testString'] = "testField1"; fieldsMapOne['testInteger'] = 123; fieldsMapOne['testBoolean'] = true; Map<String, Object> fieldsMapTwo = Map(); fieldsMapTwo['testString'] = "testField2"; fieldsMapTwo['testInteger'] = 456; fieldsMapTwo['testBoolean'] = false; List<Map<String, Object>> objectsList = new List(); objectsList.add(fieldsMapOne); objectsList.add(fieldsMapTwo); String className = "TestFlutterClass"; try { List<QBCustomObject?> customObjects = await QB.data.create(className: className, objects: objectsList); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | -------------------------------------------------- | | className | no | Name of a custom object class. | | objects | no | Custom objects list (List\<Map\<String, Object>>). | ## Retrieve records by IDs To get records with a particular record ID, use the `getByIds()` method. Set the record ID using the `ids` object. Go over [Sort operators](/sdks/flutter-custom-objects#sort-operators) and [Search operators](/sdks/flutter-custom-objects#search-operators) sections to learn about filters and search operators you can use to retrieve records. ```Dart Dart theme={null} String className = "TestFlutterClass"; List<String> ids = ["5d4175afa0eb4715cae5b63f", "5d4175afa0eb4715cae99783"]; try { List<QBCustomObject?> customObjects = await QB.data.getByIds(className, ids); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ------------------------------ | | className | yes | Name of a custom object class. | | ids | yes | Custom objects IDs. | ## Retrieve records You can search for records of a particular class. The request below will return records of the `TestFlutterClass` class with the `version` field containing `1000` value, sorted by the `created_at` field in descending order, limited to 50 records on the page, and with 10 records skipped at the beginning. ```Dart Dart theme={null} String className = "TestFlutterClass"; QBFilter filter = QBFilter(); filter.operator = QBCustomObjectsStringSearchTypes.CTN; filter.value = "1000"; filter.type = QBCustomObjectsFilterTypes.STRING; filter.field = "version"; QBSort sort = new QBSort(); sort.ascending = false; sort.field = "created_at"; int limit = 50; int skip = 10; try { List<QBCustomObject?> customObjects = await QB.data.get(className, sort: sort, filter: filter, limit: limit, skip: skip); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | ------------------------------------------------------------------------------------------- | | sort | no | Specifies sorting criteria for the field. | | filter | no | Specifies filtering criteria for the field. | | skip | no | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | no | Limit search results to N records. Useful for pagination. Default value: **100**. | If you want to retrieve only records updated after some specific date time and order the search results, you can apply operators. Thus, you can apply [search](/sdks/flutter-custom-objects#search-operators) and [sort](/sdks/flutter-custom-objects#sort-operators) operators to the list of records on the page so that it is easier to view specific records. ### Search operators You can use search operators to get more specific search results. The request below will return records of the `FlutterTestClass` class by the `flutterTestField` field with a value greater than `10`. ```Dart Dart theme={null} try { String className = "FlutterTestClass"; QBFilter filter = QBFilter(); filter.operator = QBCustomObjectsIntegerSearchTypes.GT; filter.value = "10"; filter.field = "flutterTestField"; List<QBCustomObject?> customObjects = await QB.data.get(className, filter: filter); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Description | | ---------------- | ------------------------------- | ---------------------------------------------------- | | lt | integer, float | **Less Than** operator. | | lte | integer, float | **Less Than** or **Equal** to operator. | | gt | integer, float | **Greater Than** operator. | | gte | integer, float | **Greater Than** or **Equal** to operator. | | ne | integer, float, string, boolean | **Not Equal** to operator. | | in | integer, float, string | **IN** array operator. | | or | integer, float, string | All records that contain a value 1 **or** value 2. | | nin | integer, float, string | Not **IN** array operator. | | all | array | **ALL** are contained in array. | | ctn | string | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return records of the `FlutterTestClass` class sorted by the `flutterTestField` field in descending order. ```Dart Dart theme={null} try { String className = "FlutterTestClass"; QBSort sort = QBSort(); sort.field = "flutterTestField"; sort.ascending = false; List<QBCustomObject?> customObjects = await QB.data.get(className, sort: sort); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` Here are the sort options that you can use to order the search results. | Sort options | Aplicable to types | Description | | ------------ | ------------------ | ------------------------------------------------------------------- | | ascending | All types | Sort results in ascending order by setting the ascending as true. | | descending | All types | Sort results in descending order by setting the ascending as false. | ## Update records To update a single record with a particular record ID, use the code snippet below. ```Dart Dart theme={null} String className = "TestFlutterClass"; String id = "5d4175afa0eb4715cae5b63f"; Map<String, Object> fieldsMap = Map(); fieldsMap['testString'] = "testField1"; fieldsMap['testInteger'] = 123; fieldsMap['testBoolean'] = true; try { List<QBCustomObject?> customObjects = await QB.data.update(className, id: id, fields: fieldsMap); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ------------------------------ | | className | yes | Name of a custom object class. | | id | no | Custom object ID. | | fields | no | List of updating fields. | You can update multiple records using the code snippet below. ```Dart Dart theme={null} String className = "TestFlutterClass"; Map<String, Object> fieldsMapOne = Map(); fieldsMapOne['testString'] = "testField1"; fieldsMapOne['testInteger'] = 123; fieldsMapOne['testBoolean'] = true; Map<String, Object> fieldsMapTwo = Map(); fieldsMapTwo['testString'] = "testField2"; fieldsMapTwo['testInteger'] = 456; fieldsMapTwo['testBoolean'] = false; List<Map<String, Object>> objectsList = new List(); objectsList.add(fieldsMapOne); objectsList.add(fieldsMapTwo); try { List<QBCustomObject?> customObjects = await QB.data.update(className, objects: objectsList); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ------------------------------------------- | | className | yes | Name of a custom object class. | | objects | no | Custom objects list (Map\<String, Object>). | ## Delete records To delete a single record, use the code snippet below. ```Dart Dart theme={null} String className = "TestFlutterClass"; List<String> ids = ["5d4175afa0eb4715cae5b63f"]; try { await QB.data.remove(className, ids); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ---------------------------------------- | | className | yes | Name of a custom object class. | | idsList | yes | Custom objects IDs list (List\<String>). | To delete a multiple records, use the code snippet below. ```Dart Dart theme={null} String className = "TestFlutterClass"; List<String> ids = ["5d4175afa0eb4715cae5b63f", "5d4175afa0eb4715cae99783"]; try { await QB.data.remove(className, ids); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ---------------------------------------- | | className | yes | Name of a custom object class. | | idsList | yes | Custom objects IDs list (List\<String>). | ## Relations It is possible to create a relation between objects of **two different classes** via `_parent_id` field. For example, we have the class **Rating** that contains `score`, `review`, and `comment` fields. We also have a **Movie** class. So we can create a record of class **Rating** that will point to the record of the class **Movie** via its `_parent_id` field, so the `_parent_id` field will contain the ID of record from class **Movie**. <Warning> This is not a simple soft link. This is actually a **hard link**. When you delete the **Movie** class record then all its children (records of class **Rating** with `_parent_id` field set to the **Movie** class record ID) will be automatically **deleted** as well. </Warning> <Note> If you need to retrieve all children, you can retrieve records with the filter `_parent_id=<id_of_parent_class_record>`. </Note> # Push Notifications Source: https://docs.quickblox.com/sdks/flutter-push-notifications Learn how to send push notifications to users when they are offline. Push Notifications provide a way to deliver some information to a user while they are not using your app actively. The following use cases can be covered by push notifications: * **Offline messages.** Send a chat message when a recipient is offline. In this case, a push notification will be sent automatically if the user is offline. * **Offline calls.** Make a video/audio call with offline opponents. In this case, a push notification will be sent manually. * **Requests to contact list.** Send requests to add a user to the contact list. In this case, a push notification will be sent manually). * **User tags.** Send notifications to specific user groups defined by tags. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. ## Configuration ### iOS **Step 1**. Create APNs certificate and upload it to the Dashboard. Each iOS application that uses Apple Push Notifications must have an APNs certificate. Upload the APNs certificate to QuickBlox **Dashboard => Push Notifications => Settings => Apple Push Notification Service (APNs)**. To learn how to create APNs certificates and upload them to Dashboard, refer to the [guide](/sdks/ios-push-notifications#create-apns-certificate). **Step 2**. Open your project in Xcode and enable Push Notifications capabilities. 1. Open Xcode and choose your project file. 2. Choose the **Signing & Capabilities** tab. 3. Add a **Push Notifications** and **Background Modes** capabilities. To see these sections, you should be logged in to Xcode with your Apple ID that is enrolled in the Apple developer program. 4. Turn on a **Push Notifications** capability. 5. Turn on a **Background modes** capability. This section requires you to add specific app permissions. 6. Select a **Remote notifications** checkbox if you want to receive push notifications when the app goes to background mode. 7. Select a **Voice over IP** checkbox if you want your app to receive incoming Voice-over-IP (VoIP) push notifications and use them to display the system call interface to the user when the app is in the background mode. <Frame> <img alt="Set push notifications 1.jpg" /> </Frame> ### Android **Step 1**. Configure Firebase project and get **API key** and **Sender ID**. First, you should create a Firebase account if you haven't it. Then you should configure your [Firebase project](https://console.firebase.google.com/). Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. Configure QuickBlox SDK for your app. Check out [Setup ](/sdks/flutter-setup)page for more details. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. **Step 2**. Generate a new service account key. <Frame> <img alt="Set push notifications 1.jpg" /> </Frame> To generate a new service account key, you need to: 1. Open the Firebase console and select your project. 2. Go to Project settings > Service Accounts. 3. Click on Generate new private key and confirm by clicking Generate key. 4. Securely store the JSON file containing the private key that will be downloaded **Step 3**. Navigate to the push notifications section on QuickBlox dashboard <Frame> <img alt="Set push notifications 1.jpg" /> </Frame> To navigate to the push notifications section: 1. Head over to QuickBlox dashboard, and choose your application. 2. Select the push notifications tab and navigate to the settings page. 3. Select the Service account key tab <Note> We will remove the Server key tab when it will be discontinued by FCM, you will by default only have the service account key section on the dashboard. </Note> **Step 4**. Upload the service account key on QuickBlox dashboard <Frame> <img alt="Set push notifications 1.jpg" /> </Frame> To upload your service account key: 1. Choose the environment for your service account key. (Development/Production) 2. Click on the **Browse** button and select the JSON file containing the key that was downloaded in **Step 2**. 3. Hit the **Upload** button. <Note> When setting up your environment, it’s important to distinguish between development and production modes. If you’ve uploaded a development certificate, it will only function for subscriptions created in the development environment, and likewise for production. This separation ensures seamless testing and deployment of push notifications across different environments. </Note> **Step 5**. Add Firebase to your Project. 1. As part of enabling Firebase services in your Android application, you need to add the **google-services dependency** to your **project-level** `build.gradle` file. ```groovy groovy theme={null} buildscript { // ... dependencies { // ... classpath 'com.google.gms:google-services:4.4.2' } } ``` 2. Add FCM dependency to your **app level** `build.gradle` file. ```groovy groovy theme={null} implementation(platform("com.google.firebase:firebase-bom:33.4.0")) ``` 3. Include a **gms plugin** to your **app level** `build.gradle` file. ```groovy groovy theme={null} apply plugin: 'com.google.gms.google-services' ``` 4. Download the `google-services.json` file from your Firebase Project dashboard and put it into your **app** folder in your Android project. You can also refer to the official Firebase documentation for more detailed guidance: [Add a Firebase configuration file](https://firebase.google.com/docs/android/setup#add-config-file). ## Subscribe to push notifications We recommended using the library firebase\_messaging (link: [https://pub.dev/packages/firebase\_messaging](https://pub.dev/packages/firebase%5Fmessaging)) to receive/send the push notifications. Subscribe for push notifications. Create a push subscription using the code below. ```Dart Dart theme={null} //Push Channels //QBPushChannelNames.GCM; //QBPushChannelNames.APNS; //QBPushChannelNames.APNS_VOIP; //QBPushChannelNames.EMAIL; String channelName = ""; String? token = ""; try { if (Platform.isAndroid) { token = await FirebaseMessaging.instance.getToken(); channelName = QBPushChannelNames.GCM; } else if (Platform.isIOS) { token = await FirebaseMessaging.instance.getAPNSToken(); channelName = QBPushChannelNames.APNS; } await QB.subscriptions.create(token!,channelName); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more detai } ``` <Warning> A single user can have up to **10** subscriptions on different devices. </Warning> <Note> The `deviceToken` is a device registration token generated by the APNs or GCM/FCM. The token can be unregistered by the APNs or GCM/FCM anytime. In this case, the device should be registered again and obtain a new token. When a new token is obtained, a new subscription should be created. </Note> ## Send push notifications We recommended using the library firebase\_messaging (link: [https://pub.dev/packages/firebase\_messaging](https://pub.dev/packages/firebase%5Fmessaging)) to receive/send the push notifications. You can manually initiate sending push notifications to user/users on any event in your application. To do so, you need to form push notification parameters (payload) and set the push recipients. ```Dart Dart theme={null} //Event Types //QBNotificationEventTypes.FIXED_DATE; //QBNotificationEventTypes.PERIOD_DATE; //QBNotificationEventTypes.ONE_SHOT; //Notification Event Types //QBNotificationTypes.PUSH; //QBNotificationTypes.EMAIL; //Notification Push Types //QBNotificationPushTypes.APNS //QBNotificationPushTypes.APNS_VOIP //QBNotificationPushTypes.GCM //QBNotificationPushTypes.MPNS String eventType = QBNotificationEventTypes.ONE_SHOT; String notificationEventType = QBNotificationTypes.PUSH; int pushType = QBNotificationPushTypes.APNS_VOIP; int senderId = 329873; Map<String, Object> payload = new Map(); payload["message"] = "test"; try { List<QBEvent? events = await QB.events.create(eventType, notificationEventType, senderId, payload, pushType: pushType); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | eventType | yes | Types of the notification event:- QBNotificationEventTypes.ONE\_SHOT - a one-time event (valid only if the date is not specified).- QBNotificationEventTypes.FIXED\_DATE - a one-time event that occurs at a specified date (valid only if the date is specified).- QBNotificationEventTypes.PERIOD\_DATE - is a reusable event that occurs within a given period from the initial date (valid only if the period is specified). | | notificationEventType | yes | Type of notifications:- QBNotificationTypes.PUSH - send push notification.- QBNotificationTypes.EMAIL - send email. | | senderId | yes | ID of the user who created the event. | | payload | yes | Push notification payload. Can contain a message (string) property and many other key-value pairs (string-string). Refer [here](https://docs.quickblox.com/reference/push-notifications#push-notification-formats) for more details. | | pushType | no | Channel for delivering notification. Used only if the notification type is QBNotificationTypes.PUSH, ignored in other cases. If a QBNotificationPushTypes is not present, the notification will be delivered to all possible devices/platforms for specified users. Possible values are:QBNotificationPushTypes.APNSQBNotificationPushTypes.APNS\_VOIPQBNotificationPushTypes.GCMQBNotificationPushTypes.MPNS | <Warning> You can send only FCM data messages to the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages on your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notification. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> <Warning> How to set up the FCM console and plug into QuickBlox dash board please see [How to enable Cloud Messaging API (Legacy)](/sdks/android-push-notifications#how-to-enable-cloud-messaging-api-legacy) or [HTTP v1 API](/sdks/android-push-notifications#http-v1-api) </Warning> <Note> You can send APNs VoIP notifications to the iOS app. However, if the iOS app is not subscribed to APNs VoIP notifications or the APNs VoIP certificate has expired, the regular APNs will be delivered instead of APNs VoIP. </Note> ## Recieve push notifications To receive the push notifications, we recommend using [this library](https://pub.dev/packages/firebase%5Fmessaging). To show notifications on the screen, we recommended using [this library](https://pub.dev/packages/flutter%5Flocal%5Fnotifications). ```Dart Dart theme={null} FirebaseMessaging.onMessage.listen((message) { AndroidNotificationChannel channel = const AndroidNotificationChannel("channel_id", "some_title", "some_description", importance: Importance.high); AndroidNotificationDetails details = AndroidNotificationDetails(channel.id, channel.name, channel.description, icon: "launch_background"); FlutterLocalNotificationsPlugin plugin = FlutterLocalNotificationsPlugin(); int id = message.hashCode; String title = "some message title"; String body = message.data["message"]; plugin.show(id, title, body, NotificationDetails(android: details)); }); ``` <Note> Make sure that the application has a correct icon file for notification. </Note> ## Unsubscribe from push notifications To delete a subscription, use the following code snippet. ```Dart Dart theme={null} int id = 929988234923; try { await QB.subscriptions.remove(id); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Troubleshooting #### A subscription is removed after a push is sent and the push isn't delivered **Cause**: a device registration token is invalid. <Note> The device registration token is represented as `deviceToken` within the system and is set in the `subscription`. See [this section](/sdks/flutter-push-notifications#subscribe-to-push-subscriptions) to learn how to subscribe a device to push notifications. </Note> **Tip**: check if the device registration is correct. The device registration token can be invalid due to a number of reasons: 1. Some other data is set instead of a correct device registration token. For example, a Firebase project ID, Firebase user token, etc. 2. The client app unregistered itself from GCM/FCM. This can happen if the user uninstalls the application or, on iOS, if the APNs Feedback Service reported the APNs token as invalid. 3. The registration token expired. For example, Google might decide to refresh registration tokens or the APNs token may have expired for iOS devices. 4. The client app was updated, but the new version is not configured to receive messages. For all these cases, remove the invalid device registration token and stop using it to send messages. Then, obtain a new token and make sure to create a new subscription with a valid token. # Quick Start Source: https://docs.quickblox.com/sdks/flutter-quick-start Learn how to install QuickBlox SDK and send your first message. QuickBlox SDK helps you implement real-time chat, video chat, and push notifications to your app. You can fully concentrate on your mobile app development. ## Start with sample app Choose the code sample below to jump-start the development. We use GitHub repositories to make it easy to explore, copy, and modify our code samples. The guide on how to launch and configure the sample app is on GitHub. <CardGroup> <Card title="Flutter Chat Sample App" icon="flutter"> <a href="https://github.com/QuickBlox/quickblox-flutter-samples/tree/master/chat%5Fsample"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/flutter-chat"> <Icon icon="book" /> Documentation </a> </Card> </CardGroup> ## Flutter Chat Sample App [View on GitHub](https://github.com/QuickBlox/quickblox-flutter-samples/tree/master/chat%5Fsample) [Documentation](https://docs.quickblox.com/sdks/flutter-chat) For more samples, head to our [Code Samples](/code-samples/code-samples) page. These sample apps are available on GitHub so feel free to browse them there. ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app clicking **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click **Add** button. 4. Go to **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** . ## Requirements The minimum requirements for QuickBlox Flutter SDK are: * iOS 13.0 * Android (minimum version 5.0, API 21) * Flutter (minimum version 2.12.0) ## Install QuickBlox SDK into your app <Note> To manage project dependencies [Flutter](https://flutter.dev/docs/get-started/install) should be installed. </Note> To connect QuickBlox to your app just add it into your project dependencies in **pubspec.yaml** file located in the **root project directory => dependencies** section. ```YAML YAML theme={null} dependencies: flutter: sdk: flutter # The following adds the QuickBlox SDK to your application. quickblox_sdk: 0.18.0 ``` #### ⚠️ Android – Breaking Change (from 0.18.0 and above) Starting from **0.18.0**, the SDK **no longer manages permissions** required for calling functionality on Android. If your app uses calling features, you must **manually declare** the necessary permissions in your app’s `AndroidManifest.xml` file. <Note> Kindly ensure you're modifying the main `AndroidManifest.xml` file located at the application level — not a test or variant manifest. </Note> ```xml XML theme={null} <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <!-- Permissions required for QuickBlox calling functionality --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" /> <!-- other manifest entries --> </manifest> ``` ## Send your first message ### Initialize QuickBlox SDK Initialize the framework with your application credentials. Pass `appId`, `authKey`, `authSecret`, `accountKey` to the `init()` method using the code snippet below. ```Dart Dart theme={null} String appId = 76730; String authKey = "XydaWcf8OO9xhGT"; String authSecret = "iiohfdija792hjt"; String accountKey = "7yvNe17TnjNUqDoPwfqp"; void init() async { try { await QB.settings.init(appId, authKey, authSecret, accountKey); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` <Warning> You must initialize SDK before calling any methods through the SDK, except for the `init()` method. If you attempt to call a method without connecting, the error is returned. </Warning> <Tip> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend and initialize QuickBlox SDK with applicationId and acountKey only. More details you can find in [Initialize QuickBlox SDK without Authorization Key and Secret](/sdks/flutter-setup#initialize-quickblox-sdk-without-authorization-key-and-secret) section. </Tip> ### Authorize user Now, it is time to log in with the user. To get it done, set the login and password of the user, call the `login()` method and pass the `login` and `password` to it using the code snippet below. ```Dart Dart theme={null} String login = "chrispeterson"; String password = "superPassword"; void login() async { try { QBLoginResult result = await QB.auth.login(login, password); QBUser qbUser = result.qbUser; QBSession qbSession = result.qbSession; } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` ### Connect to chat Having authorized a user, you can proceed with connecting to the chat server to start using Chat module functionality. Call the `connect()` method and pass `userId` and `password` to it. ```Dart Dart theme={null} int userId = 38457619; String password = "superPassword"; void connect() async { try { await QB.chat.connect(userId, password); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` ### Create dialog QuickBlox provides three types of dialogs: **1-1 dialog**, **group dialog**, and **public dialog**. Learn more about dialogs [here](/sdks/flutter-chat-dialogs#create-dialog). Let’s create a simple **1-1 dialog**. Call the `createDialog()` method and pass the `occupantsIds`, `dialogName`, and `dialogType` to it. ```Dart Dart theme={null} List<int> occupantsIds = [98987887, 76894569]; String dialogName = "test dialog"; int dialogType = QBChatDialogTypes.CHAT; void createDialog() async { try { QBDialog? createdDialog = await QB.chat.createDialog(occupantsIds, dialogName, dialogType: dialogType); if(createdDialog != null) { String _dialogId = createdDialog.id!; } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` ### Subscribe to receive messages QuickBlox SDK emits events to notify about chat events. Thus, when a message has been received, a user receives the event from SDK about a new incoming message. To process events, you need to provide an event handler that SDK will call. See the code snippet below. ```Dart Dart theme={null} String eventName = QBChatEvents.RECEIVED_NEW_MESSAGE; void subscribeNewMessage() async { try { await QB.chat.subscribeChatEvent(eventName, (data) { Map<dynamic, dynamic> map = Map<dynamic, dynamic>.from(data); Map<dynamic, dynamic> payload = Map<dynamic, dynamic>.from(map["payload"]); String messageId = payload["id"] as String; }, onErrorMethod: (error) { // Some error occurred, look at the exception message for more details }); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` ### Send message To send a message, call the `sendMessage()` method and pass the `dialogId` and `body` as arguments to it. ```Dart Dart theme={null} String dialogId = "8b75a6c7191285499d890a81df4ee7fe49bc732a"; String body = "hey there!"; bool saveToHistory = true; void sendMessage() async { try { await QB.chat.sendMessage(dialogId, body: body, saveToHistory: saveToHistory); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` <Note> Set the `saveToHistory` parameter if you want this message to be saved in chat history. </Note> # SDK Reference Source: https://docs.quickblox.com/sdks/flutter-sdk-reference # Setup Source: https://docs.quickblox.com/sdks/flutter-setup Learn how to add and configure QuickBlox SDK for your app. Follow the instructions below to ensure that QuickBlox SDK runs smoothly with your app. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app clicking **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click **Add** button. 4. Go to **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** . ## Requirements The minimum requirements for QuickBlox Flutter SDK are: * iOS 13.0 * Android (minimum version 5.0, API 21) * Flutter (minimum version 2.12.0) ## Install QuickBlox SDK into your app <Note> To manage project dependencies [Flutter](https://flutter.dev/docs/get-started/install) should be installed. </Note> To connect QuickBlox to your app just add it into your project dependencies in **pubspec.yaml** file located in the **root project directory => dependencies** section. ```YAML YAML theme={null} dependencies: flutter: sdk: flutter # The following adds the QuickBlox SDK to your application. quickblox_sdk: 0.18.0 ``` ## Add permissions ### For Android Starting from **0.18.0**, the SDK **no longer manages permissions** required for calling functionality on Android. This change provides developers with more control over their app's permission handling. If your app uses calling features, please make sure to **manually declare** the required permissions in your app’s `AndroidManifest.xml` file. Additionally, if your app requires access to the camera, microphone, internet, or storage, you may need to include related permissions in the manifest as well. <Note> Kindly ensure you're modifying the main `AndroidManifest.xml` file located at the application level — not a test or variant manifest. </Note> ```XML XML theme={null} <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <!-- Permissions required for QuickBlox calling functionality --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" /> <!-- Common permissions for accessing camera, microphone, network, and storage --> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> </manifest> ``` Your app requires adding some specific permissions to access the camera, microphone, internet, and storage permissions. To configure chat functionality, add permissions below: ```XML XML theme={null} <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ``` To configure video chat functionality, add permissions below: ```XML XML theme={null} <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.CAMERA"/> ``` <Note> Note that mentioning the camera and microphone permissions in the manifest isn't always enough. You need to request camera and microphone permissions additionally at runtime. </Note> ### For iOS You can use our SDK in the background mode as well. If you want to receive push notifications when the app goes to background mode, this requires you to add specific app permissions. Under the app build settings, open the **Capabilities** tab and turn on **Remote notifications** checkbox only. <Frame> <img alt="flutter-add-remote-notification-permission" /> </Frame> If you want to use video calling functionality in the background mode, set the **Audio, AirPlay, and Picture in Picture** checkboxes. <Frame> <img alt="flutter-audio-airplay-permission" /> </Frame> ## Initialize QuickBlox SDK Initialize the framework with your application credentials. Pass **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** to the `init()` method. ```Dart Dart theme={null} String appId = 76730; String authKey = "XydaWcf8OO9xhGT"; String authSecret = "iiohfdija792hjt"; String accountKey = "7yvNe17TnjNUqDoPwfqp"; void init() async { try { await QB.settings.init(appId, authKey, authSecret, accountKey); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` <Warning> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend. </Warning> | Argument | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------- | | appId | yes | Application identifier. | | authKey | yes | Authorization key. | | authSecret | yes | Authorization secret. | | accountKey | yes | Required to get actual apiEndpoint and chatEndpoint for the right server. | ## Initialize QuickBlox SDK without Authorization Key and Secret You may don't want to store authKey and authSecret inside an application for security reasons. In such case, you can initialize QuickBlox SDK with applicationId and accountKey only, and store your authKey and authSecret on your backend. But, if so, the implementation of [authentication with QuickBlox](https://docs.quickblox.com/reference/authentication) should be also moved to your backend. ```Dart Dart theme={null} String appId = 76730; String accountKey = "7yvNe17TnjNUqDoPwfqp"; void init() async { try { await QB.settings.initWithAppId(appId, accountKey: accountKey); } on PlatformException catch (e) { // some error occurred, look at the exception message for more details } } ``` | Argument | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------- | | appId | yes | Application identifier. | | accountKey | no | Required to get actual apiEndpoint and chatEndpoint for the right server. | Then using your backend you can authorize a user in the QuickBlox system, send back the user session token, and set it to the QuickBlox SDK using startSessionWithToken() method. You can find out more about this in the [Set existing session](/sdks/flutter-authentication#set-existing-session) section. ## Point SDK to enterprise server To point QuickBlox SDK to the QuickBlox enterprise server, you should pass `apiEndpoint` and `chatEndpoint` to the `init()` method. ```Dart Dart theme={null} String appId = 76730; String authKey = "XydaWcf8OO9xhGT"; String authSecret = "iiohfdija792hjt"; String accountKey = "7yvNe17TnjNUqDoPwfqp"; String apiEndpoint = "api.endpoint.com"; String chatEndpoint = "chat.enpoint.com"; void init() async { try { await QB.settings.init(appId, authKey, authSecret, accountKey, apiEndpoint: apiEndpoint, chatEndpoint: chatEndpoint); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` | Argument | Required | Description | | ------------ | -------- | ----------------------- | | appId | yes | Application identifier. | | authKey | yes | Authorization key. | | authSecret | yes | Authorization secret. | | apiEndpoint | no | API endpoint. | | chatEndpoint | no | Chat endpoint. | <Note> [Contact our sales team](https://quickblox.com/enterprise/#get) to get **API endpoint** and **chat endpoint**. </Note> ## Enable auto-reconnect to Chat QuickBlox Chat runs over XMPP protocol. To receive messages in a real time mode, the application should be connected to the Chat over XMPP protocol. To enable auto-reconnect to Chat, call `enableAutoReconnect()` method and pass `true` to it. ```Dart Dart theme={null} void enableAutoReconnect() async { try { await QB.settings.enableAutoReconnect(true); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` Thus, if XMPP connection is lost and autoreconnect functionality is enabled, the app connects to Chat automatically. You can disable autoreconnection to Chat by passing `enable` parameter as `false` to `enableAutoReconnect()` method. ```Dart Dart theme={null} void enableAutoReconnect() async { try { await QB.settings.enableAutoReconnect(false); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` <Note> **By default**, this parameter is enabled. Set autoreconnection before calling the `login()` method so it could be applied in a current chat. </Note> ## Enable logging Logging functionality allows you to keep track of all events and activities while running your app. As a result, you can monitor the operation of the SDK and improve the debug efficiency. There are 3 logging use cases: * **Server API** logging is used to monitor Server API calls. * **Chat** logging is used to monitor chat issues. * **WebRTC** logging is used to gather issues with video. 1. Go over [this section](/sdks/ios-setup#enable-logging) to learn how to enable logging for your iOS app. 2. Go over [this section](/sdks/android-setup#enable-logging) to learn how to enable logging for your Android app. Enable XMPP logging ```Dart Dart theme={null} void enableCarbons() async { try { await QB.settings.enableXMPPLogging(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` Disable XMPP logging ```Dart Dart theme={null} void enableCarbons() async { try { await QB.settings.disableXMPPLogging(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` Enable Server API logging ```Dart Dart theme={null} void enableCarbons() async { try { await QB.settings.enableLogging(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` Disable Server API logging ```Dart Dart theme={null} void enableCarbons() async { try { await QB.settings.disableLogging(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` ## Message carbons Message carbons functionality allows for multi-device support. Thus, all user messages get copied to all their devices so they could keep up with the current state of the conversation. For example, a User A has phone running conversations and desktop running conversations. User B has desktop running conversations. When User B sends a message to User A, the message shows on both the desktop and phone of User A. **Enable message carbons** ```Dart Dart theme={null} void enableCarbons() async { try { await QB.settings.enableCarbons(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` **Disable message carbons** ```Dart Dart theme={null} void disableCarbons() async { try { await QB.settings.disableCarbons(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` <Warning> **By default**, this parameter is turned off. Keep it enabled if you want to receive messages on all devices of the particular user (when a user is logged in on several devices). </Warning> ## Stream management Stream management has two important features **Stanza Acknowledgements** and **Stream Resumption**: * **Stanza Acknowledgements** is the ability to know if a stanza or series of stanzas has been received by one's peer. In other words, a reply is requested on every sent message. If the reply is received, the message is considered as delivered. * **Stream Resumption** is the ability to quickly resume a stream that has been terminated. Once a connection is re-established, Stream Resumption is executed. By matching the sequence numbers assigned to each Stanza Acknowledgement a server and client can verify which messages are missing and request to resend missing messages. Call `initStreamManagement()` method and pass `autoReconnect` and `messageTimeout` parameters to it to enable stream management. ```Dart Dart theme={null} bool autoReconnect = true; int messageTimeout = 3; void initStreamManagement() async { try { await QB.settings.initStreamManagement(messageTimeout, autoReconnect: autoReconnect); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` | Argument | Required | Description | | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | | messageTimeout | yes | Preferred resumption time (in seconds). If this parameter is greater than 0, then it is applied, otherwise it is not applied. | | autoReconnect | no | Set this parameter to apply Stream resumption. Set it as true to use Stream resumption for new connections. Default: **false**. | <Note> You should enable Stream Management before you do the `login()` because the Stream Management is initialized while Chat login is performed. The Stream Management defines an extension for active management of a stream between a client and server, including features for stanza acknowledgments. </Note> # Users Source: https://docs.quickblox.com/sdks/flutter-users Learn how to manage your users with QuickBlox. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. ## Create user It's recommended to manage user creation at your backend for production. To learn more you can refer to [QuickBlox API documentation](https://docs.quickblox.com/reference/create-user). For POCs/MVPs or during development you may want to create users on the fly, you can use `createUser()` method. Create a user using the code snippet below. Only login (or email) and password are required. Other fields are optional. ```Dart Dart theme={null} String login = "chrispeterson"; String password = "superPassword"; String email = "[[email protected]](/cdn-cgi/l/email-protection)"; int blobId = 34253543; int externalUserId = 2398734; int facebookId = 2928734; int twitterId = 92374392; String fullName = "John Smith"; String phone = "+380772342381"; String webSite = "www.moo.com"; String customData = ""; String tagList = ""; try { QBUser? user = await QB.users.createUser(login, password, email, blobId: blobId, externalUserId: externalUserId, facebookId: facebookId, twitterId: twitterId, fullName: fullName, phone: phone, website: website, customData: customData, tagList: tagList); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------------- | -------- | ----------------- | | login | yes | User login. | | password | yes | User password. | | email | no | User email. | | blobId | no | User blob ID. | | externalUserId | no | User external ID. | | facebookId | no | User facebook ID. | | twitterId | no | User twitter ID. | | fullName | no | User full name. | | phone | no | User phone. | | website | no | User website. | | customData | no | User custom data. | | tagList | no | User tag list. | <Warning> **Security & Privacy** It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. Email, full name, facebookId and phone number are PII, [configure session permissions](/docs/application#set-session-permissions) according to your privacy requirements. </Warning> ## Retrieve users Get a list of users using the `getUsers()` method. The code snippet below shows how to get a list of users created between the two given dates, sorted in descending order, and limited to 100 users on the page. ```Dart Dart theme={null} try { QBFilter filter = QBFilter(); filter.type = QBUsersFilterTypes.DATE; filter.field = QBUsersFilterFields.CREATED_AT; filter.operator = QBUsersFilterOperators.BETWEEN; filter.value = "2021-08-01T10:00:00Z, 2021-08-12T10:00:00Z"; QBSort sort = QBSort(); sort.field = QBUsersSortFields.CREATED_AT; sort.type = QBUsersSortTypes.DATE; sort.ascending = false; int page = 5; int perPage = 100; List<QBUser?> userList = await QB.users.getUsers(filter: filter, sort: sort, page: page, perPage: perPage); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` The method `getUsers()` accepts two (optional) arguments of the object type that can have the following fields: | Field | Required | | | ------- | -------- | -------------------------------------------- | | filter | no | Specifies filtering criteria for the field. | | sort | no | Specifies sorting criteria for the field. | | page | no | Number of pages with results to be returned. | | perPage | no | Number of records to return in one page. | If you want to retrieve only users updated after some specific date time, you can use operators. This is useful if you cache users somehow and do not want to obtain the whole list of your users on every app start. Thus, you can use [search](/sdks/flutter-users#search-operators) and [sort](/sdks/flutter-users#sort-operators) operators to list users on the page so that it is easier to see specific users. ### Search operators You can use search operators to get more specific search results. The request below will return a list of users with a login other than the one specified. ```Dart Dart theme={null} try { QBFilter filter = QBFilter(); filter.type = QBUsersFilterTypes.STRING; filter.field = QBUsersFilterFields.LOGIN; filter.operator = QBUsersFilterOperators.NE; filter.value = "admin"; List<QBUser?> userList = await QB.users.getUsers(filter: filter); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ | | lt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less Than** operator. | | gt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** operator. | | ge | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** or **Equal** to operator. | | le | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less or Equal to** operator | | eq | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Equal** to operator. | | ne | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Not Equal** to operator. | | between | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Contained between values** operator. | | in | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **IN** array operator. | ### Sort operators You can use sort operators to order the search results. The request below will return a list of users with the login sorted in descending order. ```Dart Dart theme={null} try { QBSort sort = QBSort(); sort.field = QBUsersSortFields.LOGIN; sort.type = QBUsersSortTypes.STRING; sort.ascending = false; List<QBUser?> userList = await QB.users.getUsers(sort: sort); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` Here are the sort options that you can use to order the search results: | Sort options | Applicable to types | Applicable to fields | Description | | ------------ | ------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- | | ascending | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Sort results in ascending order by setting the ascending as true. | | descending | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Sort results in descending order by setting the ascending as false. | ## Retrieve users by full name To get a list of users by full name for a current account, user the following code snippet. ```Dart Dart theme={null} String fullName = "test"; int page = 5; int perPage = 100; try { List<QBUser?> userList = await QB.users.getUsersByFullName(fullName, page: page, perPage: perPage ); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | ---------------------------------------------------------------------- | | fullName | yes | A string of fullName | | page | no | Number of pages with results to be returned. Default: **1**. | | perPage | no | Number of records to return in one page. Min: **1**. Default: **100**. | ## Retrieve users by tag To get a list of users by tag for a current account, use the following code snippet. ```Dart Dart theme={null} List<String> tags = ["test"]; int page = 5; int perPage = 100; try { List<QBUser?> userList = await QB.users.getUsersByTag(tags, page: page, perPage: perPage ); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | ---------------------------------------------------------------------- | | tags | yes | An array of tags. Min: **1**. | | page | no | Number of pages with results to be returned. Default: **1**. | | perPage | no | Number of records to return in one page. Min: **1**. Default: **100**. | ## Reset user password <Note> This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://help.quickblox.com/). </Note> ## Update user Update a user profile by calling the `updateUser()` method. If you want to change your password, you need to provide 2 parameters: `password` and `newPassword`. The updated `user` entity will be returned. You can also update any other field of the user using the `updateUser()` method. Thus, the snippet below shows how to update a `tagList` and `customData` fields. ```Dart Dart theme={null} String login = "chrispeterson"; String customData = ""; String tagList = "tagOne, tagTwo"; try { var someObject = {}; someObject["name"] = "John Smith"; someObject["age"] = "31"; someObject["city"] = "New York"; String customData = json.encode(someObject); QBUser? user = await QB.users.updateUser(login, customData: customData, tagList: tagList); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | ---------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | login | yes | User login. | | customData | no | User custom data. Should be a String. You can convert any data types to String, for example, JSON, XML , etc. | | tagList | no | User tags. A comma separated String without any spaces. A tag should contain alphanumeric characters and start with a letter. For example, the "tagOne" format is correct while the "tag one" format is incorrect. If more that 10 tags are provided, an error is returned: tag list should contain maximum 10 tags. | ## Set user avatar To set a user avatar, just upload a file to the QuickBlox cloud storage and connect it to the user. To upload the file to the QuickBlox cloud storage, call the `upload()` method and pass the `imageFileUrl` to it. The `imageFileUrl` is a path to the file in the device filesystem. Now that the file is uploaded, get the ID of the uploaded file. To connect the file to the user, set the ID of the uploaded file to the `blobId` field of the `user` and call the `updateUser()` method. As a result, the user avatar gets updated. ```Dart Dart theme={null} String imageFileUrl = "content://data/image.jpg"; String login = "chrispeterson"; int blobId = 324987234; try { QBFile? file = await QB.content.upload(imageFileUrl); if (file != null){ int blobId = file!.id; QBUser? updatedUser = QB.users.updateUser(login, blobId: blobId); } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` Pass the following arguments to the `updateUser()` method: | Argument | Required | Description | | --------- | -------- | ------------- | | userLogin | yes | User login. | | blobId | no | User blob ID. | ## Get user avatar Now, other users can get access to your avatar by calling the `getPrivateURL()` method. As a result, you will receive a private URL in the response. See [this section](/sdks/flutter-content#get-file-url) to learn more about file URLs. ```Dart Dart theme={null} // get access to the avatar that was set in user's "blobId" property int avatarFileId = user.blobId; try { QBFile? file = await QB.content.getInfo(avatarFileId); if (file != null){ String uid = file!.uid; String? fileUrl = await QB.content.getPrivateURL(uid); } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` ## Delete user A user can delete himself from the platform. ```Dart Dart theme={null} try { await QB.users.deleteUser(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` # Basic Source: https://docs.quickblox.com/sdks/flutter-video-calling Learn how to add peer-to-peer video calls to your app. QuickBlox Video Calling API is built on top of [WebRTC](https://webrtc.org/). It allows adding real-time video communication features into your app similar to Skype using API easily. The communication is happening between peers representing camera devices. There are two **peer types**: * **Local peer** is a device running the app right now. * **Remote peer** is an opponent device. Establishing real-time video communication between two peers involves 3 phases: 1. **Signaling**. At this phase, the peers’ local IPs and ports where they can be reached (ICE candidates) are exchanged as well as their media capabilities and call session control messages. 2. **Discovery**. At this phase, the public IPs and ports at which endpoints can be reached are discovered by STUN/TURN server. 3. **Establishing a connection**. At this phase, the data are sent directly to each party of the communication process. <Warning> In order to start using Video Calling Module, you need to **connect to QuickBlox Chat first**. The signaling in the QuickBox WebRTC module is implemented over the XMPP protocol using QuickBlox Chat Module. It acts as a signaling transport for Video Calling API. </Warning> <Note> Please use this WebRTC Video Calling to make the Group Calls with **4 or fewer users**. Because of [Mesh architecture](https://webrtcglossary.com/mesh/) we use for multi-point where every participant sends and receives its media to all other participants, the current solution supports group calls with up to 4 people. </Note> Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/flutter-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/flutter-authentication) page to learn how to do it. 4. Connect to the Chat server to provide a signaling mechanism for Video Calling API. Follow our [Chat](/sdks/flutter-chat) page to learn about chat connection settings and configuration. ## Initialize WebRTC WebRTC module allows to process calls. When a call is initiated or received, a call session is created. If the module is not initialized, it will not be able to create a call session and process calls consequently. To initialize WebRTC module call `init()` method. ```Dart Dart theme={null} try { await QB.webrtc.init(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` <Note> If you miss calling `init()` method, a `Exception` is returned: `The call service is not connected`. </Note> ## Manage calls <Warning> Each WebRTC session is assigned a unique session identifier (`sessionId`). You can get the `sessionId` from the `session` value returned from `call()`, `accept()`, `reject()`, `hangUp()` methods or from any event emitted by `QB.webrtc` module. See a full list of callbacks [here](/sdks/flutter-video-calling#event-handler). </Warning> To process events such as **incoming call**, you should subscribe to events first and assign an event handler. When you have subscribed to the event, you receive `StreamSubscription` that you should unsubscribe when you need in `dispose()` method. Learn more details about the event handler configuration in the [Event handler](#event-handler) section. ```Dart Dart theme={null} // WebRTC Events // QBRTCEventTypes.CALL // QBRTCEventTypes.CALL_END // QBRTCEventTypes.NOT_ANSWER // QBRTCEventTypes.REJECT // QBRTCEventTypes.ACCEPT // QBRTCEventTypes.HANG_UP // QBRTCEventTypes.RECEIVED_VIDEO_TRACK StreamSubscription? _callSubscription; ... @override void dispose() { if(_callSubscription != null) { _callSubscription!.cancel(); _callSubscription = null; } } ... String event = QBRTCEventTypes.CALL; try { _callSubscription = await QB.webrtc.subscribeRTCEvent(event, (data) { Map<dynamic, dynamic> payloadMap = Map<dynamic, dynamic>.from(data["payload"]); Map<dynamic, dynamic> sessionMap = Map<dynamic, dynamic>.from(payloadMap["session"]); String sessionId = sessionMap["id"]; int initiatorId = sessionMap["initiatorId"]; int callType = sessionMap["type"]; } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` <Note> The example above shows assigning one handler for all module events but you can assign separate handlers for each event. </Note> ## Initiate a call To call users, you should create a call session and start calling using `call()` method. ```Dart Dart theme={null} // Session types // QBRTCSessionTypes.VIDEO // QBRTCSessionTypes.AUDIO List<int> opponentIds = [22345, 23521]; int sessionType = QBRTCSessionTypes.VIDEO; try { QBRTCSession? session = await QB.webrtc.call(opponentIds, sessionType); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Description | Description | | ------------ | ----------- | -------------------------------------------------------------------------------------- | | opponentsIds | yes | A list of opponents IDs. | | sessionType | yes | Call type: `QB.webrtc.RTC\_SESSION\_TYPE.VIDEO`, `QB.webrtc.RTC\_SESSION\_TYPE.AUDIO`. | <Note> **Note** After this, your opponents will receive an event `QB.webrtc.EVENT_TYPE.CALL`. </Note> ## Accept a call To accept a call request, call `accept()` method and pass `sessionId` to tell SDK which call session to accept. ```Dart Dart theme={null} String sessionId = "5d4175afa0eb4715cae5b63f"; Map<String, Object> userInfo = new Map(); try { QBRTCSession? session = await QB.webrtc.accept(sessionId, userInfo: userInfo); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ------------------------ | | sessionId | yes | Call session identifier. | | userInfo | no | Custom user data. | ## Reject a call To reject a call request, use `reject()` method and pass `sessionId` parameter to tell SDK which call session to reject. ```Dart Dart theme={null} String sessionId = "5d4175afa0eb4715cae5b63f"; Map<String, Object> userInfo = new Map(); try { QBRTCSession? session = await QB.webrtc.reject(sessionId, userInfo: userInfo); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ------------------------ | | sessionId | yes | Call session identifier. | | userInfo | no | Custom user data. | ## End a call To end a call, use `hangUp()` method and pass `sessionId` parameter to tell SDK which call session to end. ```Dart Dart theme={null} String sessionId = "5d4175afa0eb4715cae5b63f"; Map<String, Object> userInfo = new Map(); try { QBRTCSession? session = await QB.webrtc.hangUp(sessionId, userInfo: userInfo); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ------------------------ | | sessionId | yes | Call session identifier. | | userInfo | no | Custom user data. | ## Release resource When you don't want to receive and process video calls, for example, when a user is logged out, you have to release `QB.webrtc` module. Call `release()` method that allows to unregister `QB.webrtc` module from receiving any video chat events and closes existing signaling channels. ```Dart Dart theme={null} try { await QB.webrtc.release(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` <Note> `release()` method should be called when a video track is no more valid. If you don't call this method, you will get a memory leak. </Note> ## Local/remote video view Set up two RTCVideoView for remote and local video tracks to be able to show the video. * A **remote video track** represents a remote peer video stream from a remote camera app. Specify initial value to RTCVideoView - `RTCVideoViewController` for the remote camera app of the remote peer. * A **local video track** represents a local peer video stream from a local camera app. Specify initial value to RTCVideoView is `RTCVideoViewController` for the local camera app of the local peer. Subscribe to `QBRTCEventTypes.RECEIVED_VIDEO_TRACK` manually. Thus, once the SDK receives data that a remote video track was received, it creates the event of `RECEIVED_VIDEO_TRACK` type with `userId` and `sessionId` properties. ```Dart Dart theme={null} //Widget RTCVideoViewController? _localVideoViewController; RTCVideoViewController? _remoteVideoViewController; ... child: Container( margin: new EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0), width: 160.0, height: 160.0, child: RTCVideoView( onVideoViewCreated: _onLocalVideoViewCreated, ), decoration: BoxDecoration(color: Colors.black54), ) ... child: Container( margin: new EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0), width: 160.0, height: 160.0, child: RTCVideoView( onVideoViewCreated: _onRemoteVideoViewCreated, ), decoration: BoxDecoration(color: Colors.black54), ) ... void _onLocalVideoViewCreated(RTCVideoViewController controller) { _localVideoViewController = controller; } void _onRemoteVideoViewCreated(RTCVideoViewController controller) { _remoteVideoViewController = controller; } ``` After this, invoke method `play()` and pass `sessionId` and `userId` parameters to it. If `userId` matches with the one in properties, the video starts playing. ```Dart Dart theme={null} String sessionId = "5d4175afa0eb4715cae5b63f"; int userId = 7832; int opponentId = 3928; Future<void> play() async { _localVideoViewController!.play(sessionId, userId); _remoteVideoViewController!.play(sessionId, opponentId); } ``` | Argument | Required | Description | | ---------- | -------- | -------------------------- | | sessionId | yes | Call session identifier. | | userId | yes | The ID of the local peer. | | opponentId | yes | The ID of the remote peer. | ## Event handler To process events such as incoming call, call reject, hang up, etc. you need to set up the event handler. The event handler processes various events that happen with the call session or peer connection in your app. The events are emitted by the WebRTC module of QuickBlox Flutter SDK. <Note> Once the WebRTC module is initialized, it can start emitting events, so you can assign event handler even before module initialization. </Note> Using the callbacks provided by the event handler, you can implement and execute the event-related processing code. For example, the `accept()` method is called when your call has been accepted by the user. This method receives information about the call session and additional key-value data about the user. QuickBlox Flutter SDK persistently interacts with the server via **XMPP** connection that works as a **signaling** transport for establishing a call between two or more peers. It receives the callbacks of the asynchronous events which happen with the call and peer connection. This allows you to track these events and build your own video calling features around them. ### Call session events The table below lists all supported call session event types. | Event type | Description | | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | QBRTCEventTypes.CALL | An incoming call event has been received by the peer after the call session has been initiated. | | QBRTCEventTypes.ACCEPT | An incoming call has been accepted by the peer. | | QBRTCEventTypes.REJECT | An incoming call has been rejected by the remote peer without accepting the call. | | QBRTCEventTypes.HANG\_UP | An accepted call has been ended by the peer by pressing the hang-up button. | | QBRTCEventTypes.RECEIVED\_VIDEO\_TRACK | A remote video track has been received by the remote peer. | | QBRTCEventTypes.PEER\_CONNECTION\_STATE\_CHANGED | A peer connection state has been changed. View all available peer connection states in the [Peer connection states](#peer-connection-states) section. | | QBRTCEventTypes.NOT\_ANSWER | No answer received from the remote peer within the timer expiration period. | | QBRTCEventTypes.CALL\_END | An accepted call has been ended. A call session was closed. | To track call session events, you should add the event handler. ```Dart Dart theme={null} //QBRTCEventTypes.CALL String event = QBRTCEventTypes.CALL; try { _callSubscription = await QB.webrtc.subscribeRTCEvent(event, (data) { Map<dynamic, dynamic> payloadMap = Map<dynamic, dynamic>.from(data["payload"]); Map<dynamic, dynamic> sessionMap = Map<dynamic, dynamic>.from(payloadMap["session"]); String sessionId = sessionMap["id"]; int initiatorId = sessionMap["initiatorId"]; int callType = sessionMap["type"]; } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } //QBRTCEventTypes.CALL_END String event = QBRTCEventTypes.CALL_END; try { _callEndSubscription = await QB.webrtc.subscribeRTCEvent(event, (data) { Map<dynamic, dynamic> payloadMap = Map<dynamic, dynamic>.from(data["payload"]); Map<dynamic, dynamic> sessionMap = Map<dynamic, dynamic>.from(payloadMap["session"]); String sessionId = sessionMap["id"]; } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } //QBRTCEventTypes.REJECT String event = QBRTCEventTypes.REJECT; try { _rejectSubscription = await QB.webrtc.subscribeRTCEvent(event, (data) { int userId = data["payload"]["userId"]; }); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } //QBRTCEventTypes.ACCEPT String event = QBRTCEventTypes.ACCEPT; try { _acceptSubscription = await QB.webrtc.subscribeRTCEvent(event, (data) { int userId = data["payload"]["userId"]; }); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } //QBRTCEventTypes.HANG_UP String event = QBRTCEventTypes.HANG_UP; try { _hangUpSubscription = await QB.webrtc.subscribeRTCEvent(event, (data) { int userId = data["payload"]["userId"]; }); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } //QBRTCEventTypes.RECEIVED_VIDEO_TRACK String event = QBRTCEventTypes.RECEIVED_VIDEO_TRACK; try { _videoTrackSubscription = await QB.webrtc.subscribeRTCEvent(event, (data) { Map<dynamic, dynamic> payloadMap = Map<dynamic, dynamic>.from(data["payload"]); int opponentId = payloadMap["userId"]; }); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } //QBRTCEventTypes.NOT_ANSWER String event = QBRTCEventTypes.NOT_ANSWER; try { _notAnswerSubscription = await QB.webrtc.subscribeRTCEvent(event, (data) { int userId = data["payload"]["userId"]; }); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } //QBRTCEventTypes.PEER_CONNECTION_STATE_CHANGED try { _peerConnectionSubscription = await QB.webrtc.subscribeRTCEvent( QBRTCEventTypes.PEER_CONNECTION_STATE_CHANGED, (data) { int state = data["payload"]["state"]; //QBRTCPeerConnectionStates.NEW = 0 //QBRTCPeerConnectionStates.CONNECTED = 1 //QBRTCPeerConnectionStates.FAILED = 2 //QBRTCPeerConnectionStates.DISCONNECTED = 3 //QBRTCPeerConnectionStates.CLOSED = 4 }); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` Each event emitted by WebRTC is an Object type having the following properties: | Argument | Required | Description | | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | type | yes | The name of the event you have subscribed to. | | payload | yes | Available if the event transmits the data. Almost all events contain **session** (to identify in which session this event occurred) and **userId** (to indicate initiator of the event) properties. | Go to the [Resources](#resources) section to see a sequence diagram for a regular call workflow. ### Peer connection state The peer connection state can change. To monitor the states of your peer connections (users), you need to add an event handler for `QB.webrtc.EVENT\_TYPE.PEER\_CONNECTION\_STATE\_CHANGED` event type. ```Dart Dart theme={null} //Peer connection states //QBRTCPeerConnectionStates.NEW //QBRTCPeerConnectionStates.CONNECTED //QBRTCPeerConnectionStates.FAILED //QBRTCPeerConnectionStates.DISCONNECTED //QBRTCPeerConnectionStates.CLOSED String event = QBRTCEventTypes.PEER_CONNECTION_STATE_CHANGED; try { _peerConnectionSubscription = await QB.webrtc.subscribeRTCEvent(event, (data) { int state = data["payload"]["state"]; String userId = map["userId"]; int state = map["state"]; }); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` The following table lists all supported peer connection states. | Connection state | Description | | -------------------------------------- | ---------------------------------------------- | | QBRTCPeerConnectionStates.NEW | Gathering information to establish connection. | | QBRTCPeerConnectionStates.CONNECTED | A peer is connected to the call session. | | QBRTCPeerConnectionStates.FAILED | A peer failed to join the call session. | | QBRTCPeerConnectionStates.DISCONNECTED | A peer is disconnected from the call session. | | QBRTCPeerConnectionStates.CLOSED | A call session is closed by the peer. | ## Resources A regular call workflow. <Frame> <img alt="Flutter SDK_initiate_accept_hang_up_call.png" /> </Frame> # Advanced Source: https://docs.quickblox.com/sdks/flutter-video-calling-advanced Learn how to mute audio and disable video. ## Mute audio Mute the audio by calling the `enableAudio()` method with `sessionId`, `userId`, and `enable` parameters. Using this method we can tell SDK to send/not send audio data from a remote or local peer in the specified call session. ```Dart Dart theme={null} String sessionId = "5d4175afa0eb4715cae5b63f"; bool enable = true; double userId = 923874; try { await QB.webrtc.enableAudio(sessionId, enable: enable, userId: userId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Desription | | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sessionId | yes | Call session identifier. | | mute | yes | Boolean parameter. true is muted, false is unmuted. | | userId | no | ID of the user. This is an optional parameter. If the userId is not passed to this method, a local audio stream is muted/unmuted. If the userId is passed, a remote audio stream is muted/unmuted (by userId provided). | ## Disable video Turn off the video by calling the `enableVideo()` method with `sessionId`, `userId`, and `enable` parameters. Using this method we can tell SDK not to send video data from a remote or local peer in the specified call session. ```Dart Dart theme={null} String sessionId = "5d4175afa0eb4715cae5b63f"; bool enable = true; double userId = 923874; try { await QB.webrtc.enableVideo(sessionId, enable: enable, userId: userId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sessionId | yes | Call session identifier. | | enable | yes | Boolean parameter. true is enabled, false is disabled. | | userId | no | ID of the user. If the userId is not passed to this method, a remote video stream is turned on/off. If userId is passed, a remote video stream is rendered/not rendered (by userId provided). | ## Mirror local video The mirror functionality allows flipping the video horizontally. To enable mirroring, use the `setMirror()` method in the `RTCVideoViewController`. ```Dart Dart theme={null} //Widget RTCVideoViewController? _localVideoViewController; child: Container( margin: new EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0), width: 160.0, height: 160.0, child: RTCVideoView( onVideoViewCreated: _onLocalVideoViewCreated, ), decoration: BoxDecoration(color: Colors.black54), ) void _onLocalVideoViewCreated(RTCVideoViewController controller) { _localVideoViewController = controller; } Future<void> setMirror(bool isMirror) async { try { _localVideoViewController?.setMirror(true); } on PlatformException catch (e) { // handel error } } ``` ## General settings You can change different settings for your calls using `QB.rtcConfig` class. All of them are listed below. ### Answer time interval If an opponent hasn't answered within an answer time interval, the `QBRTCEventTypes.NOT_ANSWER` event type will be received. The answer time interval shows how much time an opponent has to answer your call. Set the answer time interval using the code snippet below. ```Dart Dart theme={null} //Interval in seconds int interval = 15; try { await QB.rtcConfig.setAnswerTimeInterval(interval); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` <Note> **By default**, the answer time interval is 60 seconds. The **minimum** value is 10 seconds. </Note> ### Dialing time interval Dialing time interval indicates how often to notify your opponents about your call. Set the dialing time interval using the code snippet below. ```Dart Dart theme={null} //Interval in seconds int interval = 15; try { await QB.rtcConfig.setDialingTimeInterval(interval); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` <Note> **By default**, the dialing time interval is 5 seconds. The **minimum** value is 3 seconds. </Note> ## Switch audio output device You can switch an audio output. Call the `switchAudioOutput()` method and pass the type of the audio device to it. <Warning> You can switch the audio input only after receiving/creating a webrtc session. </Warning> ```Dart Dart theme={null} // Audio output // EARSPEAKER = 0 // LOUDSPEAKER = 1 // HEADPHONES = 2 // BLUETOOTH = 3 int output = QBRTCAudioOutputTypes.LOUDSPEAKER; try { await QB.webrtc.switchAudioOutput(output); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | output | yes | Type of the audio device:QBRTCAudioOutputTypes.EARSPEAKER, QBRTCAudioOutputTypes.LOUDSPEAKER, QBRTCAudioOutputTypes.HEADPHONES, QBRTCAudioOutputTypes.BLUETOOTH. | # Video Conference Source: https://docs.quickblox.com/sdks/flutter-video-conference Learn how to add video conference calls to your app. <Warning> This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). </Warning> QuickBlox provides a Multiparty Video Conferencing solution allowing to set up a video conference between 10-12 people. It is built on top of [WebRTC SFU](https://webrtcglossary.com/sfu/) technologies. Features supported: * Video/Audio Conference with 10-12 people. * Join/Rejoin video room functionality (like Skype). * Mute/Unmute audio/video stream. * Switch video input device (camera). ## Initialize In order to start working with Multiparty Video Conferencing API, you need to initialize the conference module by calling the `init()` method. The conference module allows to process conference calls. If the module is not initialized, it will not be able to create the session and process calls consequently. <Warning> You must call the `init()` method before calling any other methods. If you attempt to call a method without initializing the module, the error is returned. </Warning> ```Dart Dart theme={null} try { String conferenceServer = "your_conference_server"; await QB.conference.init(conferenceServer); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | ---------------- | -------- | ----------------------------- | | conferenceServer | yes | A conference server endpoint. | ## Manage calls To process events such as a received video track, you should subscribe to an event(s) first and assign an event handler. If you've subscribed to the event(s), you receive `StreamSubscription` that you should unsubscribe when you need, for example, in the `dispose()` method. You can subscribe to several events at the same time. However, you should handle these events as different subscriptions. For example, if you subscribe to three events, you should call three `subscribeConferenceEvent()` methods. And if you need to unsubscribe from these three events, you should have the same number of unsubscriptions. ```Dart Dart theme={null} // Conference Events // QBConferenceEventTypes.CONFERENCE_VIDEO_TRACK_RECEIVED // QBConferenceEventTypes.CONFERENCE_PARTICIPANT_RECEIVED // QBConferenceEventTypes.CONFERENCE_PARTICIPANT_LEFT // QBConferenceEventTypes.CONFERENCE_ERROR_RECEIVED // QBConferenceEventTypes.CONFERENCE_CLOSED // QBConferenceEventTypes.CONFERENCE_STATE_CHANGED StreamSubscription? _someSubscription; // Unsubscribe @override void dispose() { if(_someSubscription != null) { _someSubscription!.cancel(); _someSubscription = null; } } // Subscribe String event = QBConferenceEventTypes.CONFERENCE_CLOSED; try { someSubscription = await QB.conference.subscribeConferenceEvent(event ,(data) { String sessionId = data["payload"]["sessionId"]; }); } on PlatformException catch (e) { // Some error occured, look at the exception message for more details } ``` The table below lists all supported conference session events. | Event | Description | | --------------------------------------------------------- | ----------------------------------------------------------------- | | QBConferenceEventTypes.CONFERENCE\_VIDEO\_TRACK\_RECEIVED | A remote video track has been received by the remote participant. | | QBConferenceEventTypes.CONFERENCE\_PARTICIPANT\_RECEIVED | A new participant has joined a conference session. | | QBConferenceEventTypes.CONFERENCE\_PARTICIPANT\_LEFT | A participant has left a conference session. | | QBConferenceEventTypes.CONFERENCE\_ERROR\_RECEIVED | An error was received. | | QBConferenceEventTypes.CONFERENCE\_CLOSED | A conference session was closed. | | QBConferenceEventTypes.CONFERENCE\_STATE\_CHANGED | A conference session state has been changed. | ## Create session To be able to interact with the Video Conferencing API, you need to create a conference session by calling the `create()` method. Each conference session is tied to a specific `dialogId` taken from the QuickBlox Chat. See [this section](/sdks/flutter-chat-dialogs) for more information about dialogs. <Warning> It is important to store the current session in the `session` variable to be able to interact with the current conference session. For example, if you don't store the current session, you won't be able to hang up or reject a call. </Warning> ```Dart Dart theme={null} // Session Types // QBConferenceSessionTypes.VIDEO // QBConferenceSessionTypes.AUDIO ... QBConferenceRTCSession? _session; ... String dialogId = "1148462029"; String sessionType = QBConferenceSessionTypes.VIDEO; try { _session = await QB.conference.create(dialogId, sessionType); int sessionId = session!.id; } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | ----------- | -------- | ---------------------------------------------------------------------------------------- | | sessionType | yes | Conference session type: QBConferenceSessionTypes.VIDEO, QBConferenceSessionTypes.AUDIO. | | dialogId | yes | ID of the dialog. Taken from the QuickBlox Chat. | ## Join video room Once a conference session is created, you need to establish a call. To establish a call, you need to join an already-created conference session by calling the `joinAsPublisher()` method. This method joins the session and publishes your feed making you an active publisher in the room. Everyone in the room will be able to subscribe and receive your feed. Once the room is joined, you receive an array of participants' IDs. <Warning> Make sure to subscribe to each participant to be able to receive video/audio tracks from them. If you don't subscribe, you won't receive audio and video of the participant joined to the room. See [this section](/sdks/flutter-video-conference#subscribe) for more information. </Warning> ```Dart Dart theme={null} String sessionId = "114846dfsJKJDdls8dsfj2029"; try { List<int?> participants = await QB.conference.joinAsPublisher(sessionId); for (int i = 0; i < participants.length; i++) { int userId = participants[i]!; subscribeToParticipant(sessionId, userId); } } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | | sessionId | yes | Conference session ID. Received from the QBConferenceRTCSession object that is returned in the response to the create() method. | ## Subscribe Use the `subscribeToParticipant()` method to subscribe to a participant. ```Dart Dart theme={null} String sessionId = "114846dfsJKJDdls8dsfj2029"; int userid = 567527986; try { await QB.conference.subscribeToParticipant(sessionId, userId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ------------------------------------------------------------------------------------------------------------ | | sessionId | yes | Conference session ID. | | userId | yes | User ID. Taken from the array of participants' IDs received in the responce to the joinAsPublisher() method. | ## Unsubscribe Use the `unsubscribeFromParticipant()` method to unsubscribe from the participant's audio/video track. ```Dart Dart theme={null} String sessionId = "114846dfsJKJDdls8dsfj2029"; int userid = 567527986; try { await QB.conference.unsubscribeFromParticipant(sessionId, userId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ----------------------------------------------------------------------------------------------------------- | | sessionId | yes | Conference session ID. | | userId | yes | User ID. Taken from the array of participants' IDs received in the responce to the joinAsPublisher() mthod. | ## Set up video view Set up the `ConferenceVideoView` for remote and local video tracks to be able to show the video. The `ConferenceVideoView` allows displaying the video stream while the `ConferenceVideoViewController` allows to control the video view. Thus, you can play, release the video stream from video view. * A **remote video track** represents a remote peer video stream from a remote camera app. Specify the initial value to `ConferenceVideoView` - `RTCVideoViewController` for the remote camera app of the remote peer. There can be multiple remote video tracks in the conference call. In this case, you should set up the `ConferenceVideoView` for each remote video track individually. * A **local video track** represents a local peer video stream from a local camera app. Specify the initial value to `ConferenceVideoView` - `RTCVideoViewController` for the local camera app of the remote peer. There can be only one local video track in the conference call. ```Dart Dart theme={null} ... ConferenceVideoViewController? _localVideoViewController ConferenceVideoViewController? _remoteVideoViewController // Some widgets code ... child: new Container( margin: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0), width: 160.0, height: 160.0, child: ConferenceVideoView( onVideoViewCreated: onRemoteVideoViewCreated, ), decoration: BoxDecoration(color: Colors.black54), ) child: Container( margin: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0), width: 160.0, height: 160.0, child: ConferenceVideoView( onVideoViewCreated: onLocalVideoViewCreated, ), decoration: BoxDecoration(color: Colors.black54), ) ... void onRemoteVideoViewCreated(ConferenceVideoViewController controller) { _remoteVideoViewController = controller; } void onLocalVideoViewCreated(ConferenceVideoViewController controller) { _localVideoViewController = controller; } ... ``` Subscribe to the `QBConferenceEventTypes.CONFERENCE_VIDEO_TRACK_RECEIVED` event. Thus, once the SDK receives data that a remote video track was received, it creates the event of `CONFERENCE_VIDEO_TRACK_RECEIVED` type with `userId` and `sessionId` properties. See [this section](/sdks/flutter-video-conference#manage-calls) to learn how to subscribe the event. After this, invoke method `play()` and pass `sessionId` and `userId` parameters to it. If the `userId` matches with the one in properties, the video starts playing. ```Dart Dart theme={null} ... String sessionId = "114846dfsJKJDdls8dsfj2029"; int opponentId = 2182763; Future<void> startRenderingRemote() async { try { await remoteVideoViewController.play(sessionId, opponentId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } String sessionId = "114846dfsJKJDdls8dsfj2029"; int opponentId = 2182763; Future<void> startRenderingLocal() async { try { await localVideoViewController.play(sessionId, userId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } } ``` | Argument | Required | Description | | ---------- | -------- | -------------------------- | | sessionId | yes | Conference session ID. | | userId | yes | The ID of the local peer. | | opponentId | yes | The ID of the remote peer. | ## Mute local audio You can mute/unmute your own audio by using the `enableAudio()` method. ```Dart Dart theme={null} String sessionId = "114846dfsJKJDdls8dsfj2029"; bool enable = true; try { await QB.conference.enableAudio(sessionId, enable: enable); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | ---------- | -------- | ---------------------------------------------------------- | | sesssionId | yes | Conference session ID. | | enabled | no | Boolean parameter. Allows to enable/disable a local audio. | ## Disable local video You can enable/disable your own video by using the `enableVideo()` method. ```Dart Dart theme={null} String sessionId = "114846dfsJKJDdls8dsfj2029"; bool enable = true; try { await QB.conference.enableVideo(sessionId, enable: enable); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ---------------------------------------------------------- | | sessionId | yes | Conference session ID. | | enabled | no | Boolean parameter. Allows to enable/disable a local video. | ## Switch video input device You can switch a video input to the rear or front camera. The SDK automatically finds all cameras and chooses the two cameras with the highest video quality. Call the `switchCamera()` method to switch between the two cameras. ```Dart Dart theme={null} String sessionId = "114846dfsJKJDdls8dsfj2029"; try { await QB.conference.switchCamera(sessionId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ---------------------- | | sessionId | yes | Conference session ID. | ## Switch audio output device You can switch an audio output. Call the `switchAudioOutput()` method and pass the type of the audio device to it. <Warning> You can switch the audio input only after calling the `create()` method. </Warning> ```Dart Dart theme={null} // Audio output // EARSPEAKER = 0 // LOUDSPEAKER = 1 // HEADPHONES = 2 // BLUETOOTH = 3 int output = QBConferenceAudioOutputTypes.LOUDSPEAKER; try { await QB.conference.switchAudioOutput(output); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | output | yes | Type of the audio device:QBConferenceAudioOutputTypes.EARSPEAKER ,QBConferenceAudioOutputTypes.LOUDSPEAKER ,QBConferenceAudioOutputTypes.HEADPHONES,QBConferenceAudioOutputTypes.BLUETOOTH. | ## Leave video room To leave the video room, use the `leave()` method. After calling this method, the current session is deleted from SDK and you can't access this session any more. ```Dart Dart theme={null} String sessionId = "114846dfsJKJDdls8dsfj2029"; try { await QB.conference.leave(sessionId); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` | Argument | Required | Description | | --------- | -------- | ---------------------- | | sessionId | yes | Conference session ID. | ## Release resource If you don't want to receive and process video calls, for example, when a user is logged out or the `ConferenceVideoView` is going to close, you have to release the conference module. Call the `release()` method that allows to unregister the conference module from receiving any video conference events and closes existing signaling channels. If you want to create another conference session after the `release()` method, you should call the `init()` method first to initialize the conference module. After the module is initialized, you can create another conference session by calling the `create()` method. ```Dart Dart theme={null} ... ConferenceVideoViewController? _localVideoViewController; ConferenceVideoViewController? _remoteVideoViewController; ... try { await QB.conference.release(); // Release video views await _localVideoViewController!.release(); await _remoteVideoViewController!.release(); } on PlatformException catch (e) { // Some error occurred, look at the exception message for more details } ``` <Note> The `release()` method should be called when a video track is no more valid. If you don't call this method, you will get a memory leak. </Note> # Address Book Source: https://docs.quickblox.com/sdks/ios-address-book Learn how to store and sync the phone contact list with QuickBlox. Address Book API provides an interface to work with the phone address book. Upload it to the server and retrieve already registered QuickBlox users from your address book. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. ## Upload address book First of all, you need to upload your address book to the backend. It's a normal practice to do a full upload for the first time and then upload new contacts on future app logins. <Tabs> <Tab title="Swift"> ```Swift theme={null} let contact = QBAddressBookContact() contact.name = "Apple service" contact.phone = "1-800-275-2273" let contactsArray = [contact] let addressBook: NSOrderedSet = NSOrderedSet(array: contactsArray) QBRequest.uploadAddressBook(withUdid: nil, addressBook: addressBook, force: false, successBlock: { (addressBookUpdates) in // process response }, errorBlock: { (response) in // handle errors debugPrint("[error: \(response.error?.error)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBAddressBookContact *contact = [QBAddressBookContact new]; contact.name = @"Apple service"; contact.phone = @"1-800-275-2273"; NSMutableOrderedSet<QBAddressBookContact *>* addressBook = [NSMutableOrderedSet orderedSet]; [addressBook addObject:contact]; [QBRequest uploadAddressBookWithUdid:nil addressBook:[addressBook copy] force:NO successBlock:^(QBAddressBookUpdates * _Nonnull updates) { // process response } errorBlock:^(QBResponse * _Nonnull response) { // handle errors NSLog(@"errors=%@", response.error.error); }]; ``` </Tab> </Tabs> * You also can edit an existing contact by providing a new name for it. * You also can upload more contacts, not just all in one request. They will be added to your address book on the backend. If you want to override the whole address book on the backend just provide the `force:true:` option. * A device `UDID` is used in cases where a user has 2 or more devices and contacts sync is off. Otherwise, a user has a single global address book. ## Retrieve address book You can retrieve your uploaded address book using the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.addressBook(withUdid: nil, successBlock: { (addressBookContacts) in // process response }, errorBlock: { (response) in // handle errors debugPrint("[error: \(response.error?.error)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest addressBookWithUdid:nil successBlock:^(NSArray<QBAddressBookContact *> * _Nonnull contacts) { // process response } errorBlock:^(QBResponse * _Nonnull response) { // handle errors NSLog(@"errors=%@", response.error.error); }]; ``` </Tab> </Tabs> ## Update contacts If you need to update the name or phone number in your address book contacts, you should use the code sample below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let contactToUpdate = QBAddressBookContact() contactToUpdate.phone = "380955868675" // Existing phone number contactToUpdate.name = "QuickBlox in Ukraine" // Name to replace in the Server let contactsArray = [contactToUpdate] let contactsToUpdate: NSOrderedSet = NSOrderedSet(array: contactsArray) QBRequest.uploadAddressBook(withUdid: nil, addressBook: contactsToUpdate, force: false, successBlock: { (addressBookUpdates) in // process response }, errorBlock: { (response) in // handle errors debugPrint("[error: \(response.error?.error)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBAddressBookContact *contactToUpdate = [QBAddressBookContact new]; contactToUpdate.name = @"QuickBlox in Ukraine"; // Name to replace in the Server contactToUpdate.phone = @"380955868675"; // Existing phone number NSMutableOrderedSet<QBAddressBookContact *>* contactsToUpdate = [NSMutableOrderedSet orderedSet]; [contactsToUpdate addObject:contactToUpdate]; [QBRequest uploadAddressBookWithUdid:nil addressBook:[contactsToUpdate copy] force:NO successBlock:^(QBAddressBookUpdates * _Nonnull updates) { // process response } errorBlock:^(QBResponse * _Nonnull response) { // handle errors NSLog(@"errors=%@", response.error.error); }]; ``` </Tab> </Tabs> ## Delete contacts You can delete contacts by using the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let contactToDelete = QBAddressBookContact() contactToDelete.phone = "380955868675" // Existing phone number contactToDelete.destroy = true //To mark as Deleted let contactsArray = [contactToDelete] let contactsToDelete: NSOrderedSet = NSOrderedSet(array: contactsArray) QBRequest.uploadAddressBook(withUdid: nil, addressBook: contactsToDelete, force: false, successBlock: { (addressBookUpdates) in // process response }, errorBlock: { (response) in // handle errors debugPrint("[error: \(response.error?.error)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBAddressBookContact *contactToDelete = [QBAddressBookContact new]; contactToDelete.phone = @"380955868675"; contactToDelete.destroy = YES; //To mark as Deleted NSMutableOrderedSet<QBAddressBookContact *>* contactsToDelete = [NSMutableOrderedSet orderedSet]; [contactsToDelete addObject:contactToDelete]; [QBRequest uploadAddressBookWithUdid:nil addressBook:[contactsToDelete copy] force:NO successBlock:^(QBAddressBookUpdates * _Nonnull updates) { // process response } errorBlock:^(QBResponse * _Nonnull response) { // handle errors NSLog(@"errors=%@", response.error.error); }]; ``` </Tab> </Tabs> ## Retrieve registered users Using this request you can easily retrieve the QuickBlox users - your address book contacts that are already registered in your app. Users are matched with address book contacts by phone number, so user and address book contact must have the same phone number to be included in response. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.registeredUsersFromAddressBook(withUdid: "your udid", isCompact: false, successBlock: { (users) in // process response }, errorBlock: { (response) in // handle errors debugPrint("[error: \(response.error?.error)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest registeredUsersFromAddressBookWithUdid:@"your udid" isCompact:NO successBlock:^(NSArray<QBUUser *> *users) { // process response } errorBlock:^(QBResponse * _Nonnull response) { // handle errors NSLog(@"errors=%@", response.error.error); }]; ``` </Tab> </Tabs> If the `isCompact ` parameter is `true`, the server will return **only** the `userId` and `phone` fields of the `QBUser` model. Otherwise, all fields of the `QBUser` model will be returned. ## Push notification on new contact joined There is a way to get a push notification when some contact from your Address Book has been registered in the app. You can enable this feature at QuickBlox Dashboard. Just follow **Dashboard => *YOUR\_APP* => Users => Settings** direction and enable push notifications for new contacts. <Frame> <img alt="ios-users-settings.png" /> </Frame> # Authentication Source: https://docs.quickblox.com/sdks/ios-authentication Learn how to authenticate your users with QuickBlox. Every user needs to authenticate with QuickBlox before using any QuickBlox functionality. When someone connects with an application using QuickBlox, the application will need to obtain a session token which provides temporary secure access to QuickBlox APIs. A session token is an opaque string that identifies a user and an application. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. ## Session token rights There are different types of session tokens to support different use cases. | Session Token Type | Description | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Application session token | This kind of access token is needed to read the app data. Has only READ access to resources. The expiration time after the last REST API request is 2 hours. | | User session token | The user token is the most commonly used type of token. This kind of access token is needed any time the app calls an API to read, modify or write a specific user's data on their behalf. Has READ/WRITE access to resources. The expiration time after the last REST API request is 2 hours. | ## Session management By default, when a session gets expired, a new session with a new session token is created automatically. Thus, QuickBlox SDK stores a session token and uses it for all subsequent requests within the current session. ## Get session At any time you can get details about your current session. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSession.current.sessionDetails ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBSession.currentSession.sessionDetails ``` </Tab> </Tabs> ## Sign up user Before you log in the user, you must create the user on QuickBlox. Recommendations are below: 1. **For POCs/MVPs:** Create the user using the [QuickBlox Dashboard](https://admin.quickblox.com/) or in client app with application session token. 2. **For production apps:** Use the [QuickBlox Create User API](https://docs.quickblox.com/reference/create-user) with [API key](/docs/application#create-api-key) on your backend to create the user when your user signs up in your app. <Warning> **Security** It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. </Warning> You can create a user with application session token in client app by calling `signUp()` method. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let userInfo = QBUUser() userInfo.login = "myLogin" userInfo.fullName = "myFullName" userInfo.password = "myPassword" let user = try await QBRequest.signUp(userInfo) ``` </Tab> <Tab title="Swift"> ```Swift theme={null} let userInfo = QBUUser() userInfo.login = "myLogin" userInfo.fullName = "myFullName" userInfo.password = "myPassword" QBRequest.signUp(userInfo, successBlock: { response, user in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} QBUUser *userInfo = [[QBUUser alloc] init]; userInfo.login = @"myLogin"; userInfo.fullName = @"myFullName"; userInfo.password = @"myPassword"; [QBRequest signUp:userInfo successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull user) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Log in user QuickBlox provides four types of user authentication: login/email and password, social, phone number, and custom identity provider login. ### Login/email and password Log in a user just by using login (or email) and password. Other fields are optional. Thus, the QuickBlox server requests a users database for a match. If there is a match, a user session is created. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let user = try await QBRequest.login("userLogin", password: "userPassword") ``` </Tab> <Tab title="Swift"> ```Swift theme={null} QBRequest.logIn(withUserLogin: "userLogin", password: "userPassword", successBlock: { (response, user) in //Block with response and user instances if the request is succeeded. }, errorBlock: { (response) in //Block with response instance if the request is failed. }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} [QBRequest logInWithUserLogin:@"userLogin" password:@"userPassword" successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull tUser) { //Block with response and user instances if the request is succeeded. } errorBlock:^(QBResponse * _Nonnull response) { //Block with response instance if the request is failed. }]; ``` </Tab> </Tabs> ### Social Authenticate with QuickBlox using a social network access token. <Tabs> <Tab title="Swift"> ```Swift theme={null} let socialProvider = "facebook" let accessToken = "Az9dgLfyK7tZBSAz9dgLfyK7tQNttIoaZA10niR68DO" //Social provider's access token. QBRequest.logIn(withSocialProvider: socialProvider, accessToken: socialProviderAccessToken, accessTokenSecret: nil, successBlock: { (response, user) in //Block with response and user instances if the request is succeeded. }, errorBlock: { (response) in //Block with response instance if the request is failed. }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *socialProvider = @"facebook"; //Social provider. Posible values: facebook, twitter. NSString *accessToken = @"EAAECKEsf4G4BAGQaE4yA1basdfxxxJSUQ2HS7fUllXQj5V1jdZAykykbVMmnJ8kiiVyGTU4Spj6emBFz0mZBRlNtaJwJGhChCquYRSZBb7vmsfl64jhy7QUo54SesQWERTYUa2jDHzQDWedXjWVTbmM4pvtbsai63jgZA16iCDHKb"; //Social provider access token. [QBRequest logInWithSocialProvider:socialProvider accessToken:accessToken accessTokenSecret:nil successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull tUser) { //Block with response and user instances if the request is succeeded. } errorBlock:^(QBResponse * _Nonnull response) { //Block with response instance if the request is failed. }]; ``` </Tab> </Tabs> | Parameters | Description | | ----------------- | ------------------------------------------------------------------------------------ | | socialProvider | A social network provider. | | accessToken | An access token received from the social network after a user authenticates with it. | | accessTokenSecret | A social network provider's access token secret. | ### Phone number A sign-in with a phone number is supported with **Firebase integration**. In order to implement authentication via phone number functionality, follow this [Firebase document](https://firebase.google.com/docs/auth/web/phone-auth). Don't forget to enable phone number sign-in for your Firebase project. To learn how to do this, see this [Firebase document](https://firebase.google.com/docs/auth/web/phone-auth#enable-phone-number-sign-in-for-your-firebase-project). Send a verification code to the user's phone and sign in the user on Firebase with the received verification code. Then, log in the user to QuickBlox. To log in the user, use the `logIn()` method. Pass the project ID and ID token to the `logIn()` method. The ID token is received as a result of `getIdToken()` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} import Firebase import FirebaseUI private func performPhoneLogin() { guard let authUI = FUIAuth.defaultAuthUI() else { return } authUI.delegate = self let phoneAuth = FUIPhoneAuth(authUI: authUI) authUI.providers = [phoneAuth] phoneAuth.signIn(withPresenting: self, phoneNumber: nil) } //MARK: - FUIAuthDelegate extension AuthViewController: FUIAuthDelegate { func authUI(_ authUI: FUIAuth, didSignInWith authDataResult: AuthDataResult?, error: Error?) { if error != nil { return } guard let authDataResult = authDataResult else { return } let myFirebaseprojectID = "thisIsProjectIDFromFirebase" //Firebase project ID authDataResult.user.getIDToken(completion: { (token, completionError) in guard let token = token else { return } QBRequest.logIn(withFirebaseProjectID: myFirebaseprojectID, accessToken: token, successBlock: { (response, user) in //Block with response and user instances if the request is succeeded. }) { (response) in //Block with response instance if the request is failed. } }) } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} @import FirebaseCore; @import FirebaseAuth; @import FirebaseUI.FUIPhoneAuth; - (void)performPhoneLogin { FUIAuth *authUI = [FUIAuth defaultAuthUI]; authUI.delegate = self; FUIPhoneAuth *phoneAuth = [[FUIPhoneAuth alloc] initWithAuthUI:authUI]; authUI.providers = @[phoneAuth]; [phoneAuth signInWithPresentingViewController:self phoneNumber:nil]; } NSString *myFirebaseprojectID = @"thisIsProjectIDFromFirebase"; //Firebase project ID // MARK: - FUIAuthDelegate delegate - (void)authUI:(FUIAuth *)authUI didSignInWithAuthDataResult:(FIRAuthDataResult *)authDataResult error:(NSError *)error { if (error != nil) { return; } [authDataResult.user getIDTokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable __unused completionError) { [QBRequest logInWithFirebaseProjectID:myFirebaseprojectID accessToken:token successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull tUser) { //Block with response and user instances if the request is succeeded. } errorBlock:^(QBResponse * _Nonnull response) { //Block with response instance if the request is failed. }]; }]; } ``` </Tab> </Tabs> Pass the following arguments to the `logIn()` method. | Arguments | Description | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | myFirebaseprojectID | Firebase project ID. When you create a Firebase project, Firebase automatically assigns a unique ID to the project, but you can edit it during the project setup. | | token | ID token of the Firebase user. Created by Firebase when a user signs in to an app. This token is received as a result of getIdToken() method. | ### Custom identity provider You can authenticate your application users from the external database with QuickBlox via Custom Identity Provider (CIdP). Just specify the user login and access token as a password to authenticate with QuickBlox. Review [Custom Identity Provider](/docs/custom-identity-provider) page for more details on the feature. <Tabs> <Tab title="Swift"> ```Swift theme={null} let externalUserLogin = "myQBLogin" //External backend user ID. Posible values: externalUser.ID, externalUser.phoneNumber, externalUser.email, or any other field you would like to use as a unique identifier. let externalUserPassword = "Fd4kxd37z58dS4d2Ye7wh3" //Token received by the user during authentication on the external backend QBRequest.logIn(withUserLogin: externalUserLogin, password: externalUserPassword, successBlock: { (response, user) in //Block with response and user instances if the request is succeeded. }, errorBlock: { (response) in //Block with response instance if the request is failed. }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *externalUserLogin = @"123456789"; //External backend user ID. Posible values: externalUser.ID, externalUser.phoneNumber, externalUser.email, or any other field you would like to use as a unique identifier. NSString *externalUserPassword = @"EAAECKEsf4G4BAGQaE4yA1basdfxxxJSUQ2HS7fUllXQj5V1j..."; //Token received by the user during authentication on the external backend [QBRequest logInWithUserLogin:externalUserLogin password:externalUserPassword successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull tUser) { //Block with response and user instances if the request is succeeded. } errorBlock:^(QBResponse * _Nonnull response) { //Block with response instance if the request is failed. }]; ``` </Tab> </Tabs> <Note> This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). </Note> ## Log out user If you have a user session, you can downgrade it to an application session by calling `logout()` method. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} try await QBRequest.logOut() ``` </Tab> <Tab title="Swift"> ```Swift theme={null} QBRequest.logOut(successBlock: { (response) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} [QBRequest logOutWithSuccessBlock:^(QBResponse * _Nonnull response) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Destroy session token To destroy a session, use the following code. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.destroySession(successBlock: { (response) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest destroySessionWithSuccessBlock:^(QBResponse * _Nonnull response) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Set existing session Typically, a session token is stored in SDK after successful login and used for every subsequent API call. However, you may want to obtain and store the session on your server for better security. In this case, you can set the existing session token into SDK using the `startSession(withToken:)` method. It's can be [application or user token](https://docs.quickblox.com/reference/authentication#application-vs-user-session-token). Don't forget to [log in user](/sdks/ios-authentication#log-in-user) if you pass the application token. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSessionManager.instance.startSession(withToken:"f23d03bd2341a1f923b7d4c1fbee97af1cd296f2") ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBSessionManager.instance startSessionWithToken:@"f23d03bd2341a1f923b7d4c1fbee97af1cd296f2"]; ``` </Tab> </Tabs> Use `QBSessionManagerDelegate` to handle session life cycle. <Tabs> <Tab title="Swift"> ```Swift theme={null} class YourClass: NSObject { override init() { super.init() QBSessionManager.instance.addDelegate(self) } } extension YourClass: QBSessionManagerDelegate { func sessionManager(_ manager: QBSessionManager, didStartSessionWithDetails details: QBASession) { } func sessionManager(_ manager: QBSessionManager, didNotStartSessionWithError error: Error?) { } func sessionManagerDidExpireSession(_ manager: QBSessionManager) { } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} @interface YourClass: NSObject<QBSessionManagerDelegate> @end @implementation YourClass - (instancetype)init { self = [super init]; if (self) { [QBSessionManager.instance addDelegate:self]; } return self; } // MARK: QBSessionManagerDelegate - (void)sessionManager:(nonnull QBSessionManager *)manager didNotStartSessionWithError:(NSError * _Nullable)error { } - (void)sessionManager:(nonnull QBSessionManager *)manager didStartSessionWithDetails:(nonnull QBASession *)details { } - (void)sessionManagerDidExpireSession:(nonnull QBSessionManager *)manager { } @end; ``` </Tab> </Tabs> <Warning> If you have version lower than 2.8.0, use **QBSession** class to set the existing session token </Warning> <Tabs> <Tab title="Swift"> ```Swift theme={null} let sessionDetails = QBASession() sessionDetails.token = "f23d03bd2341a1f923b7d4c1fbee97af1cd296f2" // updateSessionBlock executes synchronously on background thread and you are allowed to execute synchronous URL request // and to block a background thread from executing until you receive updated credentials // by the end of updateSessionBlock you should call startSessionWithDetails: with updated credentials QBSession.current.start(withDetails: sessionDetails, updateSessionBlock: { // Execute synchronous URL request to retrieve new token from custom server // Until the end of this block all the operations will be paused let updatedSessionDetails = QBASession() updatedSessionDetails.token = "new token"//new token from custom server // Start Updated Session QBSession.current.start(withDetails: updatedSessionDetails) }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBASession *sessionDetails = [QBASession new]; sessionDetails.token = @"f23d03bd2341a1f923b7d4c1fbee97af1cd296f2"; // updateSessionBlock executes synchronously on background thread and you are allowed to execute synchronous URL request // and to block a background thread from executing until you receive updated credentials // by the end of updateSessionBlock you should call startSessionWithDetails: with updated credentials [[QBSession currentSession] startSessionWithDetails:sessionDetails updateSessionBlock:{ // Execute synchronous URL request to retrieve new token from custom server // Until the end of this block all the operations will be paused QBASession *updatedSessionDetails = [QBASession new]; session.token = @"new token";//new token from custom serve // Start Updated Session [[QBSession currentSession] startSessionWithDetails:updatedSessionDetails]; }]; ``` </Tab> </Tabs> # Connection Source: https://docs.quickblox.com/sdks/ios-chat-connection Learn how to connect to the chat server and set connection settings. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Connect to chat server To connect to the chat server use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let currentUser = QBUUser() currentUser.ID = 56 currentUser.password = "chatUserPass" QBChat.instance.connect(withUserID: currentUser.id, password: currentUser.password, completion: { (error) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBUUser *currentUser = [QBUUser user]; currentUser.ID = 56; currentUser.password = @"chatUserPass"; [QBChat.instance connectWithUserID:currentUser.ID password:currentUser.password completion:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> Use `QBChatDelegate` to handle different connection states. <Tabs> <Tab title="Swift"> ```Swift theme={null} class YourClass : NSObject { QBChat.instance.addDelegate(self) } // MARK: QBChatDelegate extension YourClass : QBChatDelegate { func chatDidConnect() { } func chatDidReconnect() { } func chatDidDisconnectWithError(_ error: Error?) { } func chatDidNotConnectWithError(_ error: Error) { } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} @interface YourClass () <QBChatDelegate> @end @implementation YourClass - (instancetype)init { self = [super init]; if (self) { [QBChat.instance addDelegate:self]; } return self; } // MARK: QBChatDelegate - (void)chatDidConnect { } - (void)chatDidReconnect { } - (void)chatDidDisconnectWithError:(NSError *)error { } - (void)chatDidNotConnectWithError:(NSError *)error { } - (void)chatDidFailWithStreamError:(NSError *)error { } @end; ``` </Tab> </Tabs> ## Connect to chat server with QuickBlox session token In case, you authenticate with QuickBlox via Firebase, Facebook, or Custom Identity Provider, you should connect to the chat server with QuickBlox user session token and QuickBlox user ID. As a result of successful authentication via Firebase, Facebook, or Custom Identity Provider, you receive a QuickBlox user session token and QuickBlox user ID. Use QuickBlox user session token as a password and QuickBlox user ID as a login to connect to the chat server. <Warning> Don't use the Firebase/Facebook access token as a password to connect to the chat server. You will receive the following runtime error in this case: `<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><not-authorized/><text xml:lang='en'>Password not verified</text></failure>`. </Warning> <Tabs> <Tab title="Swift"> ```Swift theme={null} let userID = QBSession.current.currentUserID let userPassword = QBSession.current.sessionDetails?.token QBChat.instance.connect(withUserID: userID, password: userPassword, completion: { (error) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSUInteger userID = QBSession.currentSession.currentUserID; NSString *userPassword = QBSession.currentSession.sessionDetails.token; [QBChat.instance connectWithUserID:userID password:userPassword completion:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | ------------ | -------- | ------------------------------------------------------------------- | | userID | yes | The ID of a user | | userPassword | yes | Specifies an active QuickBlox user session token set as a password. | ## Disconnect from chat server Disconnect from the chat server using the snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.disconnect { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance disconnectWithCompletionBlock:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> ## Enable auto-reconnect to chat Auto-reconnect is turned off by default. If you want the SDK to automatically reconnect to the chat server when the connection is lost, just enable this option: <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.autoReconnectEnabled = true ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBSettings.autoReconnectEnabled = YES; ``` </Tab> </Tabs> Once enabled, the SDK will handle reconnection automatically, no additional code needed. ## Manage chat connections To provide a seamless chat experience, our SDK manages connections to the chat server at an application-wide level. Thus, to handle [offline messages](/sdks/ios-chat-offline-messaging) correctly, use the `disconnect()` method when an app goes to the background and `connect()` method when an app goes to the foreground. As a result, the SDK will disconnect/connect the current user from the chat server when the app goes to the background/foreground mode. <Tabs> <Tab title="Swift"> ```Swift theme={null} class AppDelegate: UIResponder, UIApplicationDelegate { // ... func applicationWillTerminate(_ application: UIApplication) { QBChat.instance.disconnect { (error) in } } func applicationDidEnterBackground(_ application: UIApplication) { QBChat.instance.disconnect { (error) in } } func applicationWillEnterForeground(_ application: UIApplication) { QBChat.instance.connect(withUserID: currentUser.ID, password: currentUser.password) { (error) in } } // ... } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} @implementation AppDelegate // ... - (void)applicationWillTerminate:(UIApplication *)application { [QBChat.instance disconnectWithCompletionBlock:^(NSError * _Nullable error) { }]; } - (void)applicationDidEnterBackground:(UIApplication *)application { [QBChat.instance disconnectWithCompletionBlock:^(NSError * _Nullable error) { }]; } - (void)applicationWillEnterForeground:(UIApplication *)application { [QBChat.instance connectWithUserID:currentUser.ID password:currentUser.password completion:^(NSError * _Nullable error) { }]; } // ... ``` </Tab> </Tabs> ## Set connection settings Use the settings below to configure the connection to the chat server. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.autoReconnectEnabled = true QBSettings.carbonsEnabled = true QBSettings.keepAliveInterval = 20 QBSettings.streamManagementSendMessageTimeout = 0 QBSettings.networkIndicatorManagerEnabled = false ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBSettings setAutoReconnectEnabled:YES]; [QBSettings setReconnectTimerInterval:5]; [QBSettings setCarbonsEnabled:YES]; [QBSettings setKeepAliveInterval:20]; [QBSettings setStreamManagementSendMessageTimeout:0]; [QBSettings setNetworkIndicatorManagerEnabled:NO]; ``` </Tab> </Tabs> | Parameters | Description | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | autoReconnectEnabled | iOS SDK reconnects automatically to the chat server when the connection to the server is lost. Default: **false**. | | reconnectTimerInterval | A reconnect timer can **optionally** be used to attempt a reconnect periodically. Set in seconds. Default: **5**. | | carbonsEnabled | Message carbons allow for real-time message syncing between devices. Default: **false**. | | keepAliveInterval | Keep-alive option for a socket connection. Keep-alive is the option allowing to detect a stale connection. Set in seconds. Default: **20**. | | streamManagementSendMessageTimeout | The timeout value for stream management. Set in seconds. If this parameter is greater than 0, then it is applied, otherwise, it is not applied. Default: **0**. | | networkIndicatorManagerEnabled | A boolean value indicating whether the manager is enabled. If **true**, the manager will change status bar network activity indicator according to network operation notifications it receives. Default: **false**. | # Contact List Source: https://docs.quickblox.com/sdks/ios-chat-contact-list Learn how to create, update, manage, and add users to your contact list. The Contact List API is rather straightforward. A user A sends a request to become "friends" with a user B. The user B accepts the friend request. And now the user A and B appear in each other roster. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/ios-chat-connection) page to learn how to do it. ## Access contact list The following function gives you access to all contact list items. <Tabs> <Tab title="Swift"> ```Swift theme={null} // array of accepted contacts let contacts = QBChat.instance.contactList?.contacts // array of pending requests let pendingRequests = QBChat.instance.contactList?.pendingApproval ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // array of accepted contacts NSArray *contacts = QBChat.instance.contactList.contacts; // array of pending requests NSArray *pendingApproval = QBChat.instance.contactList.pendingApproval; ``` </Tab> </Tabs> ## Add user to your contact list To add a user to the contact list, use the following snippet. <Tabs> <Tab title="Swift"> ```Swift theme={null} let userID: UInt = 34 QBChat.instance.addUser(toContactListRequest: userID) { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSUInteger userID = 34; [QBChat.instance addUserToContactListRequest:userID completion:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> <Note> Maximum number of contacts is 300. </Note> The `QBChat` instance will call its delegate’s `chatDidReceiveContactAddRequest(fromUser:)` method and the user will receive the request to be added to the contact list. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidReceiveContactAddRequest(fromUser userID: UInt) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidReceiveContactAddRequestFromUser:(NSUInteger)userID { } ``` </Tab> </Tabs> ## Confirm the contact request To confirm the request, use `confirmAddContactRequest()` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} [QBChat.instance confirmAddContactRequest:userID completion:^(NSError * _Nullable error) { }]; ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance confirmAddContactRequest:userID completion:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> The `QBChat` instance will call `chatDidReceiveAcceptContactRequest(fromUser:)` delegate method and this user will be informed that you have accepted the contact request. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidReceiveAcceptContactRequest(fromUser userID: UInt) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidReceiveAcceptContactRequestFromUser:(NSUInteger)userID { } ``` </Tab> </Tabs> ## Reject the contact request To reject the request, use the `rejectAddContactRequest()` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.rejectAddContactRequest(userID) { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance rejectAddContactRequest:userID completion:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> The `QBChat` instance will call its `chatDidReceiveRejectContactRequest(fromUser:)` delegate method and this user will be informed that you have declined the contact request. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidReceiveRejectContactRequest(fromUser userID: UInt) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidReceiveRejectContactRequestFromUser:(NSUInteger)userID { } ``` </Tab> </Tabs> ## Remove user from the contact list To remove a previously added user from the contact list, use the following method. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.removeUser(fromContactList: 34) { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance removeUserFromContactList:34 completion:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> ## Contact list updates You can also track contact list updates in a real time by using delegates. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatContactListDidChange(_ contactList: ContactList) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatContactListDidChange:(QBContactList *)contactList { } ``` </Tab> </Tabs> # Dialogs Source: https://docs.quickblox.com/sdks/ios-chat-dialogs Learn how to create and manage dialogs. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/ios-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Dialog types All chats between users are organized in dialogs. There are 3 types of dialogs: * **private dialog** - a dialog between 2 users. * **group dialog** - a dialog between the specified list of users. * **public dialog** - an open dialog. Any user from your app can be joined to it. You need to create a new dialog and then use it to chat with other users. You also can obtain a list of your existing dialogs. ## Create dialog To create a **private** dialog, you need to set the dialog type field to `private` and ID of the opponent you want to create a chat with. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.private) dialogInfo.occupantIDs = [34] let dialog = try await QBRequest.createDialog(dialogInfo) ``` </Tab> <Tab title="Swift"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.private) dialogInfo.occupantIDs = [34] QBRequest.createDialog(dialogInfo, successBlock: { (response, dialog) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} QBChatDialog *dialogInfo = [QBChatDialog create:QBChatDialogTypePrivate]; dialogInfo.occupantIDs = @[@34]; // an ID of opponent [QBRequest createDialog:dialogInfo successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull dialog) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> To create **group** dialog for a predefined number of occupants, you need to set the dialog type field to `group` and IDs of opponents you want to create a chat with. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.group) dialogInfo.name = "New group dialog" dialogInfo.occupantIDs = [34, 45, 55] // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialogInfo.photo = "..."; let dialog = try await QBRequest.createDialog(dialogInfo) try await dialog.join() ``` </Tab> <Tab title="Swift"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.group) dialogInfo.name = "New group dialog" dialogInfo.occupantIDs = [34, 45, 55] // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialogInfo.photo = "..."; QBRequest.createDialog(dialogInfo, successBlock: { (response, dialog) in dialog.join(completionBlock: { (error) in }) }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} QBChatDialog *dialogInfo = [QBChatDialog create:QBChatDialogTypeGroup]; dialogInfo.name = @"Group dialog name"; dialogInfo.occupantIDs = @[@34, @45, @55]; // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialogInfo.photo = @"..."; [QBRequest createDialog:dialogInfo successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull dialog) { [dialog joinWithCompletionBlock:^(NSError * _Nullable error) { }]; } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> It's possible to create a **public** dialog, so any user from your application can be joined to it. There is no list of occupants. This dialog is open for everybody. You need to set the dialog type field to `public`. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.publicGroup) dialogInfo.name = "Public dialog name" dialogInfo.dialogDescription = "Public dialog description" // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialogInfo.photo = "..."; let dialog = try await QBRequest.createDialog(dialogInfo) ``` </Tab> <Tab title="Swift"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.publicGroup) dialogInfo.name = "Public dialog name" dialogInfo.dialogDescription = "Public dialog description" // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialogInfo.photo = "..."; QBRequest.createDialog(dialogInfo, successBlock: { (response, dialog) in dialog.join(completionBlock: { (error) in }) }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} QBChatDialog *dialogInfo = [QBChatDialog create:QBChatDialogTypePublicGroup]; dialogInfo.name = @"Public dialog name"; dialogInfo.dialogDescription = @"Public dialog description"; // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // dialogInfo.photo = @"..."; [QBRequest createDialog:dialogInfo successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull dialog) { [dialog joinWithCompletionBlock:^(NSError * _Nullable error) { }]; } errorBlock:^(QBResponse * _Nonnull response) { }] ``` </Tab> </Tabs> ## Create dialog with custom parameters Any dialog can be extended with additional parameters whether it is a **private**, **group**, or **public**. These parameters can be used to store additional data. Also, these parameters can be used in dialogs retrieval request. To start using additional parameters, create an additional schema of your parameters. This is a Custom Objects class. Just create an empty class with all fields that you need. These fields will be additional parameters in your dialog. See [this section](/sdks/ios-custom-objects) to learn how to create a schema using Custom Objects. Then, specify the parameters defined in the schema in a new dialog. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.group) dialogInfo.name = "Movies" dialogInfo.occupantIDs = [34, 45, 55] let customParameters = ["class_name": "Movie", "name": "Star Wars", "rating": 9.1, "documentary": false, "genre": "fantasy", "descriptions": "Star Wars is an American epic space opera."] dialogInfo.data = customParameters let dialog = try await QBRequest.createDialog(dialogInfo) ``` </Tab> <Tab title="Swift"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.group) dialogInfo.name = "Movies" dialogInfo.occupantIDs = [34, 45, 55] let customParameters = ["class_name": "Movie", "name": "Star Wars", "rating": 9.1, "documentary": false, "genre": "fantasy", "descriptions": "Star Wars is an American epic space opera."] dialogInfo.data = customParameters QBRequest.createDialog(dialogInfo, successBlock: { (response, dialog) in //Block with response and user instances if the request is succeeded. dialog.join(completionBlock: { (error) in }) }, errorBlock: { (response) in //Block with response instance if the request is failed. }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} QBChatDialog *dialogInfo = [QBChatDialog create:QBChatDialogTypeGroup]; dialogInfo.name = @"Movies"; dialogInfo.occupantIDs = @[@34, @45, @55]; NSDictionary<NSString *, id> *customParameters = @{@"class_name": @"Movie", @"name": @"Star Wars", @"rating": @(9.1), @"documentary": @(false), @"genre": @"fantasy", @"descriptions": @"Star Wars is an American epic space opera franchise consisting of a film series created by George Lucas."}; dialogInfo.data = customParameters; [QBRequest createDialog:dialogInfo successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull dialog) { //Block with response and user instances if the request is succeeded. [dialog joinWithCompletionBlock:^(NSError * _Nullable error) { }]; } errorBlock:^(QBResponse * _Nonnull response) { //Block with response instance if the request is failed. }]; ``` </Tab> </Tabs> ## Create group dialog with join required <Warning> Available since **QuickBlox iOS SDK v2.22.0**. Prior to **server version 2.34.0**, all group dialogs required joining. Starting from **server version 2.34.0**, new applications do not require joining, while existing applications retain the previous behavior. You can change the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). If `isJoinRequired` is explicitly set when creating a dialog, the provided value takes priority over the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). Most applications do not need this feature. The default behavior where participants can send and receive real-time messages without joining is recommended for most use cases. </Warning> When creating a **group** dialog, you can set the `isJoinRequired` parameter to `true` to require participants to explicitly join the dialog before they can send or receive real-time messages. This is only needed when you want to restrict real-time messaging in specific dialogs until participants explicitly join. By default, `isJoinRequired` is `false` and participants can message without joining. This parameter applies **only to group dialogs**. You can change the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.group) dialogInfo.isJoinRequired = true dialogInfo.name = "Group dialog name" dialogInfo.occupantIDs = [34, 45, 55] let dialog = try await QBRequest.createDialog(dialogInfo) let isJoinRequired = dialog.isJoinRequired ``` </Tab> <Tab title="Swift"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.group) dialogInfo.isJoinRequired = true dialogInfo.name = "Group dialog name" dialogInfo.occupantIDs = [34, 45, 55] QBRequest.createDialog(dialogInfo, successBlock: { (response, dialog) in let isJoinRequired = dialog.isJoinRequired }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} QBChatDialog *dialogInfo = [QBChatDialog create:QBChatDialogTypeGroup]; dialogInfo.isJoinRequired = YES; dialogInfo.name = @"Group dialog name"; dialogInfo.occupantIDs = @[@34, @45, @55]; [QBRequest createDialog:dialogInfo successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull dialog) { BOOL isJoinRequired = dialog.isJoinRequired; } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Check if join required for group dialog <Info> The `isJoinRequired` field is available starting from **QuickBlox iOS SDK v2.22.0**. See [Create group dialog with join required](/sdks/ios-chat-dialogs#create-group-dialog-with-join-required) for details. </Info> You can get the `isJoinRequired` value for any group dialog: <Tabs> <Tab title="Swift"> ```Swift theme={null} let isJoinRequired = groupDialog.isJoinRequired ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} BOOL isJoinRequired = groupDialog.isJoinRequired; ``` </Tab> </Tabs> ## Join group dialog <Info> Starting from **QuickBlox iOS SDK v2.22.0**, joining a group dialog is required only when `isJoinRequired` is set to `true` for a dialog. See [Create group dialog with join required](/sdks/ios-chat-dialogs#create-group-dialog-with-join-required) for details. </Info> If `isJoinRequired` is set to `true` for a group dialog, you need to join it by calling the `join()` method before you can send or receive real-time messages. See [this section](/sdks/ios-chat-messaging#send-text-message) to learn how to send/receive real-time messages. You must join the dialog after every new connection or reconnection. If the connection is lost and then restored, whether manually or automatically, you need to call `join()` again for each dialog where `isJoinRequired` is `true`. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} try await groupDialog.join() ``` </Tab> <Tab title="Swift"> ```Swift theme={null} groupDialog.join { (error) in } ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} [groupDialog joinWithCompletionBlock:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> <Note> You can join a group dialog **only** if your user ID is present in the `occupantIDs` array in the dialog model. Your user ID is added to the `occupantIDs` array if you create a dialog **or** you are added to the dialog by another user. See [this section](/sdks/ios-chat-dialogs#add-occupants) to learn how to add occupants to the group dialog. </Note> To subscribe to the `onJoinOccupant` event, use the code snippet below. As a result, you will be notified by the SDK about the `onJoinOccupant()` event whenever the occupant has joined the current group dialog. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.onJoinOccupant = { (userID) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} chatDialog.onJoinOccupant = ^(NSUInteger userID) { }; ``` </Tab> </Tabs> ## Join public dialog Before you start chatting in a **public** dialog, you must join it by calling the `join()` method. Unlike group dialogs, joining a public dialog is always required. If you've successfully joined the dialog, you can send/receive real-time messages. See [this section](/sdks/ios-chat-messaging#send-text-message) to learn how to send/receive real-time messages. You must join the dialog after every new connection or reconnection. If the connection is lost and then restored, whether manually or automatically, you need to call `join()` again. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} try await publicDialog.join() ``` </Tab> <Tab title="Swift"> ```Swift theme={null} publicDialog.join { (error) in } ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} [publicDialog joinWithCompletionBlock:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> ## Leave group dialog You can leave the **group** dialog by calling the `leave()` method. After leaving, you will stop receiving real-time messages from this dialog. You need to join the dialog again to resume receiving real-time messages. <Info> Starting from **QuickBlox iOS SDK v2.22.0**, leaving a group dialog is only needed when `isJoinRequired` is set to `true`. If `isJoinRequired` is `false`, you do not need to call `leave()`. </Info> <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} try await groupDialog.leave() ``` </Tab> <Tab title="Swift"> ```Swift theme={null} groupDialog.leave { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [groupDialog leaveWithCompletionBlock:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> <Note> When you leave a group dialog, your user ID is still present in the `occupantIDs` array in the dialog model. The dialog will still appear in the list of dialogs and you will still have access to the chat history. To remove yourself from the group dialog, use the `update()` method. See [this section](/sdks/ios-chat-dialogs#remove-occupants) to learn how to remove occupants from the group dialog. </Note> To subscribe to the `onLeaveOccupant` event, use the code snippet below. As a result, you will be notified by the SDK about the `onLeaveOccupant()` event whenever the occupant has left the current group dialog. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.onLeaveOccupant = { (userID) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} chatDialog.onLeaveOccupant = ^(NSUInteger userID) { }; ``` </Tab> </Tabs> ## Leave public dialog You can leave the **public** dialog by calling the `leave()` method. After leaving, you will stop receiving real-time messages from this dialog. You need to join the dialog again to resume receiving real-time messages. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} try await publicDialog.leave() ``` </Tab> <Tab title="Swift"> ```Swift theme={null} publicDialog.leave { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [publicDialog leaveWithCompletionBlock:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> ## Retrieve online users You can get a list of online users from the `chatDialog` dialog. Call the `requestOnlineUsers()` method to get the list of online users who are joined to the dialog. As a result, a completion block with an array of user IDs or failure error is called. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.requestOnlineUsers { (onlineUsersIDs, error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [chatDialog requestOnlineUsersWithCompletionBlock:^(NSMutableArray<NSNumber *> *onlineUsers, NSError *error) { }]; ``` </Tab> </Tabs> Let's see, how the `requestOnlineUsers()` method is used with regard to the dialog type. | Capabilities | Public | Group | Private | | --------------------- | ------ | ----- | ------- | | Retrieve online users | ✗ | ✓ | ✗ | <Note> You can retrieve online users from the group dialog **only** if are joined to it. </Note> ## Retrieve list of dialogs It's common to request all your dialogs on every app login. The request below will return **private**, **group**, and **public** dialogs that have been updated during the last month, sorted by the `last_message_date_sent` in descending order, and limited to 10 dialogs per page. <Tabs> <Tab title="Swift"> ```Swift theme={null} //get dialogs that have been updated during the last month and sort by the date of the last message in descending order let monthAgoDate = Calendar.current.date( byAdding: .month, value: -1, to: Date()) let timeInterval = monthAgoDate!.timeIntervalSince1970 let paramSort = "sort_desc" let sortValue = "last_message_date_sent" let paramFilter = "updated_at[gte]" let filterValue = "\(timeInterval)" var extendedRequest: [String: String] = [:] extendedRequest[paramSort] = sortValue extendedRequest[paramFilter] = filterValue let responsePage = QBResponsePage(limit: 10) QBRequest.dialogs(for: responsePage, extendedRequest: extendedRequest, successBlock: { response, dialogs, dialogsUsersIDs, page in }, errorBlock: { response in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //get dialogs that have been updated during the last month and sort by the date of the last message in descending order NSDate *monthAgoDate = [NSCalendar.currentCalendar dateByAddingUnit:NSCalendarUnitMonth value:-1 toDate:[NSDate date] options:0]; NSTimeInterval timeInterval = [monthAgoDate timeIntervalSince1970]; NSString *paramSort = @"sort_desc"; NSString *sortValue = @"last_message_date_sent"; NSString *paramFilter = @"updated_at[gte]"; NSString *filterValue = @(timeInterval).stringValue; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; extendedRequest[paramSort] = [NSString stringWithFormat:@"%@", sortValue]; extendedRequest[paramFilter] = [NSString stringWithFormat:@"%@", filterValue]; QBResponsePage *responsePage = [QBResponsePage responsePageWithLimit:10]; [QBRequest dialogsForPage:responsePage extendedRequest:extendedRequest successBlock:^(QBResponse *response, NSArray *dialogs, NSSet *dialogsUsersIDs, QBResponsePage *page) { } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | responsePage | no | If you want to get a paginated list of users from the server, you can set the following fields of the responsePage:- skip allows to skip N records in search results. Default (if not specified): **0**.- limit allows limit search results to N records. Default value: **100**. | | extendedRequest | yes | A dictionary that stores keys and values of the String type. The keys are formed as parameters of the [List Dialogs](https://docs.quickblox.com/reference/chat#list-dialogs) request. | If you want to retrieve only dialogs updated after some specific date time and order the search results, you can apply operators. This is useful if you cache dialogs somehow and do not want to obtain the whole list of dialogs on every app start. Thus, you can apply [search](/sdks/ios-chat-dialogs#search-operators) and [sort](/sdks/ios-chat-dialogs#sort-operators) operators to list dialogs on the page so that it is easier to view specific dialogs. The operators are set as key-value parameters in the `extendedRequest` dictionary. ### Search operators You can use search operators to get more specific search results. The request below will return 10 dialogs that were updated over the last month. <Tabs> <Tab title="Swift"> ```Swift theme={null} //get dialogs that have been updated over the last month let monthAgoDate = Calendar.current.date( byAdding: .month, value: -1, to: Date()) let timeInterval = monthAgoDate!.timeIntervalSince1970 let paramFilter = "updated_at[gte]" let filterValue = "\(timeInterval)" var extendedRequest: [String: String] = [:] extendedRequest[paramFilter] = filterValue let responsePage = QBResponsePage(limit: 10) QBRequest.dialogs(for: responsePage, extendedRequest: extendedRequest, successBlock: { response, dialogs, dialogsUsersIDs, page in }, errorBlock: { response in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //get dialogs that have been updated during the last month and sort by the date of the last message in descending order NSDate *monthAgoDate = [NSCalendar.currentCalendar dateByAddingUnit:NSCalendarUnitMonth value:-1 toDate:[NSDate date] options:0]; NSTimeInterval timeInterval = [monthAgoDate timeIntervalSince1970]; NSString *paramFilter = @"updated_at[gte]"; NSString *filterValue = @(timeInterval).stringValue; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; extendedRequest[paramFilter] = [NSString stringWithFormat:@"%@", filterValue]; QBResponsePage *responsePage = [QBResponsePage responsePageWithLimit:10]; [QBRequest dialogsForPage:responsePage extendedRequest:extendedRequest successBlock:^(QBResponse *response, NSArray *dialogs, NSSet *dialogsUsersIDs, QBResponsePage *page) { } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | --------------------------------------------------- | ---------------------------------------------------- | | lt | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** operator. | | lte | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** or **Equal** to operator. | | gt | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** operator. | | gte | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** or **Equal** to operator. | | ne | number, string, date | \_id, name, last\_message\_date\_sent | **Not Equal** to operator. | | in | number, string, date | type, last\_message\_date\_sent, name | **IN** array operator. | | nin | number, string, date | last\_message\_date\_sent | **IN** array operator. | | all | number, string, date | last\_message\_date\_sent | **ALL** are contained in array. | | ctn | number, string, date | name | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return dialogs sorted in descending order by the `last_message_date_sent` field. <Tabs> <Tab title="Swift"> ```Swift theme={null} let paramSort = "sort_desc" let sortValue = "last_message_date_sent" var extendedRequest: [String: String] = [:] extendedRequest[paramSort] = sortValue let responsePage = QBResponsePage(limit: 10) QBRequest.dialogs(for: responsePage, extendedRequest: extendedRequest, successBlock: { response, dialogs, dialogsUsersIDs, page in }, errorBlock: { response in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *paramSort = @"sort_desc"; NSString *sortValue = @"last_message_date_sent"; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; extendedRequest[paramSort] = [NSString stringWithFormat:@"%@", sortValue]; QBResponsePage *responsePage = [QBResponsePage responsePageWithLimit:10]; [QBRequest dialogsForPage:responsePage extendedRequest:extendedRequest successBlock:^(QBResponse *response, NSArray *dialogs, NSSet *dialogsUsersIDs, QBResponsePage *page) { } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> Here are the sort operators that you can use to order the search results: | Sort operator | Applicable to types | Applicable to fields | Description | | ------------- | ------------------- | ------------------------------------------------ | ------------------------------------------------------------------------- | | sort\_asc | All types | id, created\_at, name, last\_message\_date\_sent | Search results will be sorted in ascending order by the specified field. | | sort\_desc | All types | id, created\_at, name, last\_message\_date\_sent | Search results will be sorted in descending order by the specified field. | ## Update dialog You can update the information for a **private**, **group**, and **public** dialog. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.name = "New dialog name" chatDialog.dialogDescription = "New dialog description" chatDialog.photo = "https://new_photo_url" // or it can be an ID to some file in Storage module QBRequest.update(chatDialog, successBlock: { (response, updatedDialog) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} dialog.name = @"New dialog name"; dialog.dialogDescription = @"New dialog description"; dialog.photo = @"https://new_photo_url"; // or it can be an ID to some file in Storage module [QBRequest updateDialog:dialog successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull updatedDialog) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | ------------------------ | -------------- | -------------- | -------------- | | Update a dialog name | Owner | Owner | ✗ | | Update a photo | Owner | Owner | ✗ | | Update custom parameters | Owner,Occupant | Owner,Occupant | Owner,Occupant | ## Add occupants Set `pushOccupantsIDs` field to add occupants to the **group** dialog. As a result, the user ID will be added to the `occupantIDs` array. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.pushOccupantsIDs = ["10056", "75432"] QBRequest.update(chatDialog, successBlock: { (response, updatedDialog) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} chatDialog.pushOccupantsIDs = @[@"10056", @"75432"]; [QBRequest updateDialog:chatDialog successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull updatedDialog) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Parameters | Required | Description | | ---------- | -------- | ----------------------------------------------- | | chatDialog | Yes | Specifies chatDialog fields that should be set. | Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | --------------- | ------------- | -------------- | -------------- | | Add other users | ✗ | Owner,Occupant | ✗ | ## Remove occupants Set the `pullOccupantsIDs` field to remove occupants from the **group** dialog. As a result, the user ID will be removed from the `occupantIDs` array. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.pullOccupantsIDs = ["10056", "75432"] QBRequest.update(chatDialog, successBlock: { (response, updatedDialog) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} chatDialog.pullOccupantsIDs = @[@"10056", @"75432"]; [QBRequest updateDialog:chatDialog successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull updatedDialog) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Parameters | Required | Description | | ---------- | -------- | ----------------------------------------------- | | chatDialog | Yes | Specifies chatDialog fields that should be set. | Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | ------------------ | ------------- | -------------- | -------------- | | Remove other users | ✗ | Owner | ✗ | | Remove yourself | ✗ | Owner,Occupant | ✗ | ## Delete dialog A request below will remove a dialog for a current user, but other users will be still able to chat there. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let result = try await QBRequest.deleteDialogs(withIDs: ["5356c64ab35c12bd3b108a41", "d256c64ab35c12bd3b108bc5"], forAllUsers: true) ``` </Tab> <Tab title="Swift"> ```Swift theme={null} QBRequest.deleteDialogs(withIDs: Set<String>(["5356c64ab35c12bd3b108a41", "d256c64ab35c12bd3b108bc5"]), forAllUsers: false, successBlock: { (response, deletedObjectsIDs, notFoundObjectsIDs, wrongPermissionsObjectsIDs) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} [QBRequest deleteDialogsWithIDs:[NSSet setWithArray:@[@"5356c64ab35c12bd3b108a41", @"d256c64ab35c12bd3b108bc5"]] forAllUsers:NO successBlock:^(QBResponse * _Nonnull response, NSArray<NSString *> * _Nonnull deletedObjectsIDs, NSArray<NSString *> * _Nonnull notFoundObjectsIDs, NSArray<NSString *> * _Nonnull wrongPermissionsObjectsIDs) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> Set the `forAllUsers` parameter as `true` to completely remove the dialog for all users. You can also delete multiple dialogs in a single request. Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public | Group | Private | | ----------------------------------------------------------- | ------ | -------------- | -------------- | | Delete a dialog for all usersusing the forAllUsersparameter | Owner | Owner | Owner | | Delete a dialog for a current user | Owner | Owner,Occupant | Owner,Occupant | ## Get number of dialogs You can get a number of dialogs using the `countOfDialogs(withExtendedRequest:)` method. The request below will return a count of dialogs updated over the last month. <Tabs> <Tab title="Swift"> ```Swift theme={null} //get dialogs that have been updated during the last month and sort by the date of the last message in descending order let monthAgoDate = Calendar.current.date( byAdding: .month, value: -1, to: Date()) let timeInterval = monthAgoDate!.timeIntervalSince1970 let paramFilter = "updated_at[gte]" let filterValue = "\(timeInterval)" var extendedRequest: [String: String] = [:] extendedRequest[paramFilter] = filterValue QBRequest.countOfDialogs(withExtendedRequest: extendedRequest, successBlock: { response, count in }, errorBlock: { response in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //get dialogs that have been updated during the last month and sort by the date of the last message in descending order NSDate *monthAgoDate = [NSCalendar.currentCalendar dateByAddingUnit:NSCalendarUnitMonth value:-1 toDate:[NSDate date] options:0]; NSTimeInterval timeInterval = [monthAgoDate timeIntervalSince1970]; NSString *paramFilter = @"updated_at[gte]"; NSString *filterValue = @(timeInterval).stringValue; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; extendedRequest[paramFilter] = [NSString stringWithFormat:@"%@", filterValue]; [QBRequest countOfDialogsWithExtendedRequest:extendedRequest successBlock:^(QBResponse * _Nonnull response, NSUInteger count) { } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | extendedRequest | yes | A dictionary that stores keys and values of the String type. The keys are formed as patameters of the [List Dialogs](https://docs.quickblox.com/reference/chat#list-dialogs) request. | ## Get number of unread messages To get a number of unread messages from a particular dialog, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let unreadMessagesCount = dialog.unreadMessagesCount ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSUInteger unreadMessagesCount = chatDialog.unreadMessagesCount; ``` </Tab> </Tabs> You can also retrieve the **total** number of unread messages using the `totalUnreadMessageCountForDialogs()` method . It returns the total count of unread messages for all dialogs of the user in `totalUnreadCount`. It also returns unread messages count for dialogs with IDs in `dialogsDictionary` if any `dialogIDs` are specified. <Tabs> <Tab title="Swift"> ```Swift theme={null} let dialogIDs: Set<String> = ["8b23aa4f5d0b0be0900041aa", "1c23aa4f5d0b0be0900041ad"] QBRequest.totalUnreadMessageCountForDialogs(withIDs: dialogIDs, successBlock: { ( response, totalUnreadCount, dialogsDictionary) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest totalUnreadMessageCountForDialogsWithIDs:[NSSet setWithArray:@[@"8b23aa4f5d0b0be0900041aa", @"1c23aa4f5d0b0be0900041ad"]] successBlock:^(QBResponse * _Nonnull response, NSUInteger count, NSDictionary<NSString *,id> * _Nonnull dialogs) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | dialogIDs | yes | IDs of dialogs.- If dialogIDs are **not** specified, the total number of unread messages for **all** dialogs of the user will be returned.- If dialogIDs are specified, the number of unread messages for each specified dialog will be returned. Also, the total number of unread messages for all dialogs of the user will be returned. | ## Resources A sequence of steps a user takes to start a dialog by moving through the application lifecycle. <img /> # Messaging Source: https://docs.quickblox.com/sdks/ios-chat-messaging Learn how to send and receive messages, mark messages as delivered or read, etc. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/ios-chat-connection) page to learn how to do it. 5. Create a dialog. See [Dialogs](/sdks/ios-chat-dialogs) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Subscribe message events Add the event delegate to receive messages in real-time using the `addDelegate()` method. The event delegate enables the app to listen to message events associated with receiving a message, delivery receipts, and read receipts. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.addDelegate(self) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance addDelegate: self]; ``` </Tab> </Tabs> You should also implement the `QBChatDelegate` methods in your chat controller to track events in your chat. <Tabs> <Tab title="Swift"> ```Swift theme={null} /// MARK: - QBChatDelegate extension YourViewController: QBChatDelegate { // MARK: - Manage chat receive message callback's func chatRoomDidReceive(_ message: QBChatMessage, fromDialogID dialogID: String) { // Called whenever group chat dialog did receive a message. // !!!note Will be called on both recipients' and senders' device (with corrected time from server) } func chatDidReceive(_ message: QBChatMessage) { // Called whenever new private message was received from QBChat. // !!!note Will be called only on recipient device } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} /// MARK: - QBChatDelegate // MARK: - Manage chat receive message callback's - (void)chatDidReceiveMessage:(QBChatMessage *)message { // Called whenever new private message was received from QBChat. // !!!note Will be called only on recipient device } - (void)chatRoomDidReceiveMessage:(QBChatMessage *)message fromDialogID (NSString*)dialogID { // Called whenever group chat dialog did receive a message. // !!!note Will be called on both recepients' and senders' device (with corrected time from server) } ``` </Tab> </Tabs> ## Send text message To send a message to a **private** dialog, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let message = QBChatMessage() message.text = "How are you today?" message.customParameters["save_to_history"] = true let privateDialog = ... privateDialog.send(message) { (error) in } //MARK: ChatDelegate func chatDidReceive(_ message: QBChatMessage) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatMessage *message = [[QBChatMessage alloc] init]; message.text = @"How are you today?"; message.customParameters[@"save_to_history"] = @"1"; QBChatDialog *privateDialog = ...; [privateDialog sendMessage:message completionBlock:^(NSError * _Nullable error) { }]; //MARK: ChatDelegate - (void)chatDidReceiveMessage:(QBChatMessage *)message { } (edited) ``` </Tab> </Tabs> To send a message to a **group** or **public** dialog, use the code snippet below. <Tip> You need to join the **group** and **public** dialog by calling the `join()` method before you start chatting in a dialog. Once the dialog is joined, you can receive/send messages. See [this section](/sdks/ios-chat-dialogs#join-dialog) to learn how to join the dialog. </Tip> <Tabs> <Tab title="Swift"> ```Swift theme={null} let message = QBChatMessage() message.text = "How are you today?" message.customParameters["save_to_history"] = true let groupDialog = ... groupDialog.send(message) { (error) in } //MARK: ChatDelegate func chatRoomDidReceive(_ message: QBChatMessage, fromDialogID dialogID: String) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatMessage *message = [[QBChatMessage alloc] init]; message.text = @"How are you today?"; message.customParameters[@"save_to_history"] = @"1"; QBChatDialog *groupDialog = ...; [groupDialog sendMessage:message completionBlock:^(NSError * _Nullable error) { }]; //MARK: QBChatDelegate - (void)chatRoomDidReceiveMessage:(QBChatMessage *)message fromDialogID:(NSString *)dialogID { } ``` </Tab> </Tabs> <Tip> Make sure to set the `save_to_history` as `true` to save the message on the server. If the `save_to_history` is set as `false`, the message won't be saved on the server. However, the message will be delivered to the user in either case. </Tip> ## Send message with attachment Chat attachments are supported by the [content API](/sdks/ios-content). In order to send a chat attachment, you need to upload the file to QuickBlox cloud storage and obtain a link to the file (file UID). Then you need to include this UID into the chat message and send it. <Tabs> <Tab title="Swift"> ```Swift theme={null} let url = URL(fileURLWithPath:"file_path") //for image QBRequest.uploadFile(with: url, fileName: fileName, contentType: "image/png", isPublic: true, successBlock: { (response, uploadedBlob) in let attachment = QBChatAttachment() attachment.id = uploadedBlob.uid attachment.name = uploadedBlob.name //for image attachment.type = "image" attachment.url = uploadedBlob.publicUrl() let message = QBChatMessage() message.text = "Image Attachment" //Set attachment message.attachments = [attachment] //Send message with attachment chatDialog.send(message, completionBlock: { (error) in }) }, statusBlock: { (request, status) in //Update UI with upload progress }, errorBlock: { (response) in //show upload error }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSURL *fileUrl = [NSURL fileURLWithPath:@"file_path"]; //Local file url //for image [QBRequest uploadFileWithUrl:fileUrl fileName:@"image.png" contentType:@"image/png" isPublic:YES successBlock:^(QBResponse * _Nonnull response, QBCBlob * _Nonnull blob) { //Create attachment QBChatAttachment *attachment = [[QBChatAttachment alloc] init]; attachment.ID = blob.UID; //for image attachment.type = @"image"; attachment.url = blob.publicUrl; //Create message QBChatMessage *message = [[QBChatMessage alloc] init]; message.text = @"Image attachment"; //Set attachment message.attachments = @[attachment]; //Send message with attachment [chatDialog sendMessage:message completionBlock:^(NSError * _Nullable error) {}]; } statusBlock:^(QBRequest * _Nonnull request, QBRequestStatus * _Nonnull status) { //Update UI with upload progress } errorBlock:^(QBResponse * _Nonnull response) { //show upload error }]; ``` </Tab> </Tabs> The same flow is supported on the message receiver's side. When you receive a message with an attachment, you need to get the file UID, and then download the file from the cloud storage. <Tabs> <Tab title="Swift"> ```Swift theme={null} func chatDidReceive(_ message: QBChatMessage) { if let attachment = message.attachments?.first, let uid = attachment.id { QBRequest.downloadFile(withUID: uid, successBlock: { (response, data) in //process file data }, statusBlock: { (response, status) in //Update UI with dowonload progress }, errorBlock: { (response) in //show download error }) } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)chatDidReceiveMessage:(QBChatMessage *)message { QBChatAttachment *attachment = message.attachments.firstObject; [QBRequest downloadFileWithUID:attachment.ID successBlock:^(QBResponse * _Nonnull response, NSData * _Nonnull fileData) { //process file data } statusBlock:^(QBRequest * _Nonnull request, QBRequestStatus * _Nonnull status) { //Update UI with download progress } errorBlock:^(QBResponse * _Nonnull response) { //show download error }]; } ``` </Tab> </Tabs> ## Send message with extra data You have an option to extend the message with additional fields. Specify one or more **key-value** items in the `message`. Using these items, you can implement the ability for a user to send self-location information to another user or notification messages signifying that a user has left a group, etc. <Tabs> <Tab title="Swift"> ```Swift theme={null} let message = QBChatMessage() message.text = "How are you today?" message.customParameters["save_to_history"] = "1" message.customParameters["customParam1"] = "book" message.customParameters["customParam2"] = "21" let dialog = ... dialog.send(message) { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatMessage *message = [[QBChatMessage alloc] init]; message.text = @"How are you today?"; message.customParameters[@"save_to_history"] = @"1"; message.customParameters[@"customParam1"] = @"book"; message.customParameters[@"customParam2"] = @"21"; QBChatDialog *dialog = ...; [dialog sendMessage:message completionBlock:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | -------------------------------------------- | | message | yes | Specifies message fields that should be set. | Set the following fields of the `message`: | Field | Required | Description | | ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | text | no | Message text. | | customParameters | no | Extra data. Specify any key-value pairs. In each pair, the key and value are both string values. Set the save\_to\_history as 1 to save a message to the history. | ## Retrieve chat history Every dialog stores its chat history that you can retrieve using the `messages(withDialogID:extendedRequest:)` method. The request below will return messages for a specific dialog, sorted by the `date_sent` field in descending order, limited to 50 dialogs on the page. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let page = QBResponsePage(limit: 50, skip: 100) let extendedRequest = ["sort_desc": "date_sent", "mark_as_read": "0"] let result = try await QBRequest.messages(withDialogID: "5356c64ab35c12bd3b108a41", extendedRequest: extendedRequest, for: page) ``` </Tab> <Tab title="Swift"> ```Swift theme={null} let page = QBResponsePage(limit: 50, skip: 100) let extendedRequest = ["sort_desc": "date_sent", "mark_as_read": "0"] QBRequest.messages(withDialogID: "5356c64ab35c12bd3b108a41", extendedRequest: extendedRequest, for: page, successBlock: { (response, messages, page) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} QBResponsePage *responsePage = [QBResponsePage responsePageWithLimit:50 skip:0]; NSDictionary *extReq = @{@"sort_desc": @"date_sent", @"mark_as_read": @"0"}; [QBRequest messagesWithDialogID:@"5356c64ab35c12bd3b108a41" extendedRequest:extReq forPage:responsePage successBlock:^(QBResponse *response, NSArray *messages, QBResponsePage *page) { } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> <Warning> If you want to mark all retrieved chat messages as a read, set the `mark_as_read` parameter as `1` in the `extendedRequest`. If you decide not to mark chat messages as read, just set the `mark_as_read` parameter as `0`. </Warning> If you want to retrieve only messages updated after some specific date time and order the search results, you can apply operators. This is useful if you cache messages somehow and do not want to obtain the whole list of messages on every app start. Thus, you can apply [search](/sdks/ios-chat-messaging#search-operators) and [sort](/sdks/ios-chat-messaging#sort-operators) operators to list messages on the page so that it is easier to view specific messages. The operators are set as key-value parameters in the `extendedRequest` dictionary. If you want to get a paginated list of messages from the server, you can set the key-value parameters in the `extendedRequest` dictionary. | Pagination parameter | Description | | -------------------- | ------------------------------------------------------------------------------------------- | | skip | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | Limit search results to N records. Useful for pagination. Default value: **100**. | ### Search operators You can use search operators to get more specific search results. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | ---------------------------------------------------- | ---------------------------------------------------- | | lt | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** operator. | | lte | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** or **Equal** to operator. | | gt | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** operator. | | gte | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** or **Equal** to operator. | | ne | number, string, date | \_id, message, date\_sent, sender\_id, recipient\_id | **Not Equal** to operator. | | in | number, string, date | date\_sent, sender\_id, recipient\_id | **IN** array operator. | | nin | number, string, date | date\_sent, sender\_id, recipient\_id | Not **IN** array operator. | | or | number, string, date | date\_sent, sender\_id, recipient\_id | All records that contain a value 1 **or** value 2. | | ctn | string | message | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. | Sort operator | Applicable to types | Description | | ------------- | ------------------- | ------------------------------------------------------------------------- | | sort\_asc | All types | Search results will be sorted in ascending order by the specified field. | | sort\_desc | All types | Search results will be sorted in descending order by the specified field. | ## Update message Update the message text using the `updateMessage(withID:text:dialogID:)` method below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let messageId = "5b23aa4f5d0b0be0900041aa" let text = "Edited text" let dialogId = "5356c64ab35c12bd3b108a41" QBRequest.updateMessage(withID: messageId, text: text, dialogID: dialogId, successBlock: { (response) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *messageId = @"5b23aa4f5d0b0be0900041aa"; NSString *text = @"Edited text"; NSString *dialogId = @"5356c64ab35c12bd3b108a41"; [QBRequest updateMessageWithID: messageId text: text dialogID: dialogId successBlock:^(QBResponse * _Nonnull response) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Delete message Any user in the `occupantIDs` can delete a message from the dialog. As a result, the message will be deleted from the current user history, without affecting the histories of other users. The owner of the dialog can completely remove messages from all users' histories. This is achieved by setting the `forAllUsers` parameter to `true`. <Tabs> <Tab title="Swift"> ```Swift theme={null} let messages: Set<String> = ["5b23aa4f5d0b0be0900041aa", "bc23aa4f5d0b0be0900041ad"] QBRequest.deleteMessages(withIDs: messages, forAllUsers: forAllUsers, successBlock: { (response) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSSet *messages = [NSSet setWithArray:@[@"5b23aa4f5d0b0be0900041aa", @"bc23aa4f5d0b0be0900041ad"]]; [QBRequest deleteMessagesWithIDs:messages forAllUsers:NO successBlock:^(QBResponse * _Nonnull response) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------------------------------------ | | messages | yes | A set of strings with messages IDs | | forAllUser | yes | A boolean parameter. Delete message for everyone. Set it as true to perform. Only the owner can do it. | ## Check if a message is sent The message is considered as **sent** if it has been delivered to the server. To get to know that a message has been delivered to the server, make sure to enable a stream management before connecting to the Chat server. See [this section](/sdks/ios-setup#stream-management) to learn how to enable the stream management. Thus, you send a message to the server and if no error is returned, it is considered as **sent** (by default). There is no field for a sent status in the message model. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.send(message) { (error) in if error == nil { // status - 'sent' } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatDialog *chatDialog = ...; [chatDialog sendMessage:message completionBlock:^(NSError * _Nullable error) { if(!error) { //status - 'sent' } }]; ``` </Tab> </Tabs> <Warning> You should enable Stream Management before you do the `login()` because the Stream Management is initialized while Chat login is performed. The Stream Management defines an extension for active management of a stream between a client and server, including features for stanza acknowledgments. </Warning> ## Mark message as delivered As a sender, you may want to be informed that a message has been successfully delivered to the recipient. The mark-as-delivered functionality allows to notify the sender about message delivery. To track the event when the message has been delivered to the user, use the `chatDidDeliverMessage(messageID:dialogID:)` method of the `QBChatDelegate` delegate. <Tabs> <Tab title="Swift"> ```Swift theme={null} // ... QBChat.instance.addDelegate(self) // ... //MARK: QBChatDelegate func chatDidDeliverMessage(withID messageID: String, dialogID: String, toUserID userID: UInt) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // ... [QBChat.instance addDelegate:self]; // ... //MARK: QBChatDelegate - (void)chatDidDeliverMessageWithID:(NSString *)messageID dialogID:(NSString *)dialogID toUserID:(NSUInteger)userID { } ``` </Tab> </Tabs> Use the `mark(asDelivered:)` method to mark a message as delivered. As a result, the server will notify a sender about the delivery receipt. <Tabs> <Tab title="Swift"> ```Swift theme={null} let receivedMessage: QBChatMessage = ... QBChat.instance.mark(asDelivered: receivedMessage) { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatMessage *receivedMessage = ...; [QBChat.instance markAsDelivered:receivedMessage completion:^(NSError * _Nullable error){ }]; ``` </Tab> </Tabs> | Argument | Required | Description | | --------------- | -------- | ----------------------------------- | | receivedMessage | yes | A message received from the sender. | A message can be marked as delivered automatically by the server once a message is successfully delivered to the recipient. Set the `markable` as `true` using the `send()` method if you want, as a sender, to receive message delivery receipts from other recipients. Thus, the `markable` parameter enables the sender to request the delivery receipt. It also enables the recipient to confirm the message delivery. However, if `markable` is `false` or omitted, then you can notify a sender about the delivery receipt using the `mark(asDelivered:)` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} let message = QBChatMessage() message.markable = true message.text = "How are you today?" chatDialog.send(message) { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatMessage *message = [QBChatMessage new]; message.markable = YES; message.text = @"How are you today?"; [chatDialog sendMessage:message completionBlock:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> <Tip> Make sure to understand, that marking-as-delivered operation just confirms the fact of message delivery. The message acquires the **delivered** status when the message delivered event is received. When a message is marked as delivered, the IDs of users who have received the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who received the message using the `messages(withDialogID:)` method. See [this section](/sdks/ios-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Tip> You can also update the `delivered_ids` field in the message model, on the server, by using the method below. As a result, the ID of the user who has received the message will be added to the array of `delivered_ids` field. However, the server won't notify the user about the change in the message model. <Tabs> <Tab title="Swift"> ```Swift theme={null} let dialog = ... let dialogID = dialog.id let receivedMessage: QBChatMessage = ... let receivedMessageID: String = receivedMessage.id let markMessagesAsDeliveredSet = Set<String>([receivedMessageID]) QBRequest.markMessages(asDelivered: markMessagesAsDeliveredSet, dialogID: dialogID, successBlock: { (response) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatDialog *dialog = ...; NSString *dialogID = dialog.ID; QBChatMessage *receivedMessage = ...; NSString *receivedMessageID = receivedMessage.ID; NSSet<NSString *> *markMessagesAsDeliveredSet = [NSSet setWithArray:@[receivedMessageID] [QBRequest markMessagesAsDelivered:markMessagesAsDeliveredSet dialogID:dialogID successBlock:^(QBResponse * _Nonnull response) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | -------------------------- | -------- | ----------------------------------- | | markMessagesAsDeliveredSet | yes | A set of strings with messages IDs. | | dialogID | yes | The ID of the dialog. | ## Mark message as read As a sender, you may want to be informed that a message has been read by the recipient. The mark-as-read functionality allows to notify the sender that a message has been read. To track the event when the message has been read by the user, the `chatDidReadMessage(messageID:dialogID:readerID:)` method of the `QBChatDelegate` is used. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidReadMessage(withID messageID: String, dialogID: String, readerID: UInt) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidReadMessageWithID:(NSString *)messageID dialogID:(NSString *)dialogID readerID:(NSUInteger)readerID { } ``` </Tab> </Tabs> Use the `read()` method to mark a message as read. As a result, the server will notify a sender about the read receipt. <Tabs> <Tab title="Swift"> ```Swift theme={null} let receivedMessage: QBChatMessage = ... QBChat.instance.read(receivedMessage) { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatMessage *receivedMessage = ...; [QBChat.instance readMessage:receivedMessage completion:^(NSError * _Nullable error) { } ``` </Tab> </Tabs> | Argument | Required | Description | | --------------- | -------- | ----------------------------------- | | receivedMessage | yes | A message received from the sender. | <Check> When a message is marked as read, the IDs of users who have read the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who read the message using the `messages(withDialogID:)` method. See [this section](/sdks/ios-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Check> You can also update the `read_ids` field in the message model, on the server, by using the method below. As a result, the ID of the user who has read the message will be added to the array of `read_ids` field. However, the server won't notify the user about the change in the message model. <Tabs> <Tab title="Swift"> ```Swift theme={null} let dialog = ... let dialogID = dialog.id let receivedMessage: QBChatMessage = ... let receivedMessageID: String = receivedMessage.id let markMessagesAsReadSet = Set<String>([receivedMessageID]) QBRequest.markMessages(asRead: markMessagesAsReadSet, dialogID: dialogID, successBlock: { (response) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatDialog *dialog = ...; NSString *dialogID = dialog.ID; QBChatMessage *receivedMessage = ...; NSString *receivedMessageID = receivedMessage.ID; NSSet<NSString *> *markMessagesAsReadSet = [NSSet setWithArray:@[receivedMessageID] [QBRequest markMessagesAsRead:markMessagesAsReadSet dialogID:dialogID successBlock:^(QBResponse * _Nonnull response) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | --------------------- | -------- | -------------------------------------------- | | markMessagesAsReadSet | yes | A set of strings with received messages IDs. | | dialogID | yes | The ID of the dialog that received messages. | ## Send typing indicators You may want, as a sender, to let the recipient know that you are typing the message or have stopped typing the message. Use typing indicators as a form of chat-specific presence. Typing indicators allow to indicate if users are typing messages in a dialog at the moment. There are the following **typing** notifications supported. * **typing**. The user is composing a message. The user is actively interacting with a message input interface specific to this chat session (for example, by typing in the input area of a chat window). * **stopped**. The user had been composing but now has stopped. The user has been composing but has not interacted with the message input interface for a short period of time (for example, 30 seconds). To track the event when the sender is typing the message, use the `onUserIsTyping(userID:)` method of the delegate. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.onUserIsTyping = { (userID: UInt) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} chatDialog.onUserIsTyping = ^(NSUInteger userID) { }; ``` </Tab> </Tabs> To track the event when the sender has stopped typing, use the `onUserStoppedTyping(userID:)` method of the delegate. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.onUserStoppedTyping = { (userID: UInt) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} chatDialog.onUserStoppedTyping = ^(NSUInteger userID) { }; ``` </Tab> </Tabs> To notify a recipient that a sender is typing the message, use the `sendUserIsTyping()` method. As a result, the server will notify a recipient about the event. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.sendUserIsTyping() ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [chatDialog sendUserIsTyping]; ``` </Tab> </Tabs> To notify a recipient that a sender had been composing a message but now has stopped, use the `sendUserStopTyping()` method. As a result, the server will notify a recipient about the event. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.sendUserStopTyping() ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [chatDialog sendUserStoppedTyping]; ``` </Tab> </Tabs> ## Send system messages There is a way to send system messages to other users about some events. For example, a system message can be sent when a user has joined or left a group dialog. System messages are handled over a separate channel and are not mixed up with regular chat messages. Thus, they are handled by `chatDidReceiveSystemMessage()` callback of `QBChatDelegate`. See [this section](/sdks/ios-chat-messaging#subscribe-message-events) to learn how to add `QBChatDelegate`. System messages are also not shown in the dialog history and, consequently, are not stored on the server. This means that these messages will be delivered **only** to online users. Send system messages using the `sendSystemMessage()` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} enum MessageType : String { case createGroupDialog = "1" case addUsersToGroupDialog = "2" case leaveGroupDialog = "3" } var message: QBChatMessage = QBChatMessage() let params = NSMutableDictionary() params["notification_type"] = MessageType.leaveGroupDialog.rawValue params["dialog_id"] = "5356c64ab35c12bd3b108a41" message.customParameters = params message.recipientID = 344 QBChat.instance.sendSystemMessage(message) { (error) in } #pragma mark - #pragma mark QBChatDelegate func chatDidReceiveSystemMessage(message: QBChatMessage!) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} typedef NS_ENUM(NSUInteger, NotificationMessageType) { NotificationMessageTypeCreate = 1, NotificationMessageTypeAdding = 2, NotificationMessageTypeLeave = 3 }; QBChatMessage *message = [QBChatMessage message]; NSMutableDictionary *params = [NSMutableDictionary dictionary]; params[@"notification_type"] = [NSString stringWithFormat:@"%@", @(NotificationMessageTypeLeave)]; params[@"dialog_id"] = @"5356c64ab35c12bd3b108a41"; [message setCustomParameters:params]; [message setRecipientID:344]; [[QBChat instance] sendSystemMessage:message completion:^(NSError * _Nullable error) { }]; #pragma mark - #pragma mark QBChatDelegate - (void)chatDidReceiveSystemMessage:(QBChatMessage *)message{ } ``` </Tab> </Tabs> | Argument | Description | | -------- | --------------------------------------------------- | | message | Specifies system message fields that should be set. | Set the following fields of the `message`: | Field | Required | Description | | ---------------- | -------- | -------------------------------------------------------------------------------------------------------- | | recipientID | yes | ID of the recipient. | | customParameters | no | Extra data. Specify one or more key-value pairs. In each pair, the key and value are both string values. | # Offline Messaging Source: https://docs.quickblox.com/sdks/ios-chat-offline-messaging Learn how to receive push notifications when the app is not running. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/ios-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Enable offline messaging Send notifications about new messages to the user when their app is not running by using offline messaging functionality. This functionality allows to customize automatic push notifications displayed on a user's device when they are offline. In other words, if your opponent is offline when you are writing a message, they automatically receive a push notification. <Tip> Make sure to subscribe your users to pushes using SDKs. Review [Push Notifications](/sdks/ios-push-notifications) section for more details. </Tip> <Warning> Only FCM data messages can be sent from the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages in your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notifications. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> Enable offline messaging from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Check the **Enable automatic push notifications for offline users** if you want to receive push notifications from the server automatically. <Frame> <img alt="Enable Offline Messaging" /> </Frame> ## Configure offline messaging template You can configure an offline messaging template from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Choose the template in the **Templates** section. <Note> Available for Growth/HIPAA Cloud/Enterprise plan You can set the custom template in the **Offline messaging text** section using the following template variables: * %unread\_count% - total number of new messages the recipient has * %plural\[new message]% - words in brackets (20 characters max) will be pluralized based on unread messages count * %body\[x]% - quote x characters from message body * %recipient\_fullname% - full name of recipient * %sender\_fullname% - full name of sender * %recipient\_login% - login of recipient * %sender\_login% - login of sender * %dialog\_name% - name of chat dialog (will be replaced with "Private dialog" string for 1-1 messages) </Note> 1. Set **Badge counter** to include counter info into your push message. Useful to include an unread counter that is the number of unread messages a user has. 2. Set push notification sound in the **Sound** section (for iOS only). 3. Set the **Category (iOS)** field. Enter "actions" that should be presented in the app notification in various views (for iOS only). 4. Enable the **Content Available** option to indicate that new content is available (for iOS only). 5. Enable the **Mutable content** option to modify the content of newly delivered notifications before the user is alerted. Use a notification service app extension to be able to modify the content of the notification. If the option is enabled, the system passes the notification to the service app extension for the subsequent modification of the notification payload before the actual delivery (for iOS only). 6. Click the **Save** button. <Frame> <img alt="configure_template.png" /> </Frame> <Warning> Currently, push notifications are supported in a mobile environment only. </Warning> # Privacy List Source: https://docs.quickblox.com/sdks/ios-chat-privacy-list Learn how to create privacy lists and implement user-to-user blocks. Privacy list API allows enabling or disabling communication with other users in a chat. You can create, modify, delete privacy lists or define a default list. <Note> The user can have multiple privacy lists, but only one can be active. </Note> Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/ios-chat-connection) page to learn how to do it. ## Create privacy list A privacy list must have at least one element in order to be created. If no elements are specified, then the list with a given name will be deleted. <Tabs> <Tab title="Swift"> ```Swift theme={null} let userID: UInt = 34 let privateChatPrivacyItem = QBPrivacyItem.init(privacyType: .userID, userID: userID, allow: false) privateChatPrivacyItem.mutualBlock = true let groupChatPrivacyItem = QBPrivacyItem.init(privacyType: .groupUserID, userID: userID, allow: false) let privacyList = QBPrivacyList.init(name: "PrivacyList", items: [privateChatPrivacyItem, groupChatPrivacyItem]) //Setting privacy list QBChat.instance.setPrivacyList(privacyList) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSUInteger userID = 34; QBPrivacyItem *privateChatPrivacyItem = [[QBPrivacyItem alloc] initWithPrivacyType:QBPrivacyTypeUserID userID:userID allow:NO]; privateChatPrivacyItem.mutualBlock = YES; QBPrivacyItem *groupChatPrivacyItem = [[QBPrivacyItem alloc] initWithPrivacyType:QBPrivacyTypeGroupUserID userID:userID allow:NO]; QBPrivacyList *privacyList = [[QBPrivacyList alloc] initWithName:@"PrivacyList" items:@[privateChatPrivacyItem,groupChatPrivacyItem]]; //Setting privacy list [QBChat.instance setPrivacyList:privacyList]; ``` </Tab> </Tabs> If the privacy list is set successfully, the `QBChat` instance will call `chatDidSetPrivacyList(withName:)` delegate method. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidSetPrivacyList(withName name: String) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidSetPrivacyListWithName:(NSString *)name { } ``` </Tab> </Tabs> In case of error the `QBChat` instance will call the `chatDidNotSetPrivacyList(withName:error:)` delegate method. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidNotSetPrivacyList(withName name: String, error: Error) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidNotSetPrivacyListWithName:(NSString *)name error:(NSError *)error { } ``` </Tab> </Tabs> <Note> In order to be used the privacy list should be not only set but also activated (set as default). </Note> ## Activate privacy list In order to activate rules from a privacy list, you should set it as default. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.setDefaultPrivacyListWithName("PrivacyList") ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance setDefaultPrivacyListWithName:@"PrivacyList"]; ``` </Tab> </Tabs> If the privacy list is activated, the`QBChat` instance will call the `chatDidSetDefaultPrivacyList(withName:)` method of deledate. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBQBChatDelegate func chatDidSetDefaultPrivacyList(withName name: String) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidSetDefaultPrivacyListWithName:(NSString *)name { } ``` </Tab> </Tabs> Otherwise, the `QBChat` instance will call `chatDidNotSetDefaultPrivacyList(withName:error:)` delegate method. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidNotSetDefaultPrivacyList(withName name: String, error: Error) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidNotSetDefaultPrivacyListWithName:(NSString *)name error:(NSError *)error { } ``` </Tab> </Tabs> ## Update privacy list There are some rules you should follow to update a privacy list: * Include **all** of the desired items (not a "delta"). * If you want to update or set a new privacy list instead of the current one, you should decline the current default list first. <Tabs> <Tab title="Swift"> ```Swift theme={null} //Deactivating privacy list before update QBChat.instance.setDefaultPrivacyListWithName(nil) //Some updates here //.... //Activating privacy list QBChat.instance.setDefaultPrivacyListWithName("PrivacyList") ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //Deactivating privacy list before update [QBChat.instance setDefaultPrivacyListWithName:nil]; //Some updates here //.... //Activating privacy list [QBChat.instance setDefaultPrivacyListWithName:@"PrivacyList"]; ``` </Tab> </Tabs> ## Retrieve privacy lists To get a list of **all** your privacy lists names, use the following request. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.retrievePrivacyListNames() ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance retrievePrivacyListNames]; ``` </Tab> </Tabs> If the privacy list names are retrieved successfully, the `QBChat` instance will call `chatDidReceivePrivacyListNames()` delegate method. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidReceivePrivacyListNames(_ listNames: [String]) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidReceivePrivacyListNames:(NSArray<NSString *> *)listNames { } ``` </Tab> </Tabs> Otherwise, the `QBChat` instance will call `chatDidNotReceivePrivacyListNamesDue(toError:)` delegate method. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidNotReceivePrivacyListNamesDue(toError error: Error) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidNotReceivePrivacyListNamesDueToError:(NSError *)error { } ``` </Tab> </Tabs> ## Retrieve privacy list by name To get the privacy list by name, you should use the following method. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.retrievePrivacyList(withName: "PrivacyList") ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance retrievePrivacyListWithName:@"PrivacyList"]; ``` </Tab> </Tabs> If the privacy list is retrieved successfully, the `QBChat` instance will call `chatDidReceive(_ privacyList:)` its delegate method. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidReceive(_ privacyList: QBPrivacyList) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidReceivePrivacyList:(QBPrivacyList *)privacyList { } ``` </Tab> </Tabs> Otherwise, the `QBChat` instance will call `chatDidNotReceivePrivacyList(withName:error)` delegate method. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidNotReceivePrivacyList(withName name: String, error: Error) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidNotReceivePrivacyListWithName:(NSString *)name error:(NSError *)error { } ``` </Tab> </Tabs> ## Remove privacy list To delete a list, you can call the method below or you can edit a list and set items to `nil`. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.removePrivacyList(withName: "PrivacyList") ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance removePrivacyListWithName:@"PrivacyList"]; ``` </Tab> </Tabs> If the privacy list is removed successfully, the `QBChat` instance will call `chatDidRemovedPrivacyList(withName:)` delegate method. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: QBChatDelegate func chatDidRemovedPrivacyList(withName name: String) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: QBChatDelegate - (void)chatDidRemovedPrivacyListWithName:(NSString *)name { } ``` </Tab> </Tabs> <Note> Before deleting the privacy list, you should decline it. </Note> ## Blocked user attempts to communicate with user A user can be blocked in **1-1 dialog** and **group dialog**. In this case, the blocked user receives an error when trying to send a message in a **1-1 dialog** and receives nothing when trying to send a message in **group dialog**. <Tabs> <Tab title="Swift"> ```Swift theme={null} chatDialog.onBlockedMessage = { (error) in } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} chatDialog.onBlockedMessage = ^(NSError * _Nullable error) { }; ``` </Tab> </Tabs> # User Presence Source: https://docs.quickblox.com/sdks/ios-chat-user-presence Learn how to track user presence updates and check user status using ping. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/ios-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Subscribe to contact presence updates You can track the contact presence updates in real-time using the `chatDidReceiveContactItemActivity(_ userID:isOnline:status:)` method of the `QBChatDelegate`. However, you can track the presence updates of only those users who have been added to the contact list. See [this section](/sdks/ios-chat-contact-list) to learn how to implement the Contact List. <Tabs> <Tab title="Swift"> ```Swift theme={null} //MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() QBChat.instance.addDelegate(self) } //MARK: QBChatDelegate func chatDidReceiveContactItemActivity(_ userID: UInt, isOnline: Bool, status: String?) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} //MARK: - Life Cycle - (void)viewDidLoad { [super viewDidLoad]; [QBChat.instance addDelegate:self]; } //MARK: QBChatDelegate - (void)chatDidReceiveContactItemActivity:(NSUInteger)userID isOnline:(BOOL)isOnline status:(NSString *)status { } ``` </Tab> </Tabs> ## Ping user QuickBlox SDK can send application-level pings to a user. As a result, you can check if the user is connected to the Chat server. <Tabs> <Tab title="Swift"> ```Swift theme={null} let userId = 221809 let timeout = 20.0 QBChat.instance.pingUser(withID: userId, timeout: timeout) { (timeInterval, success) in // time interval of ping and whether it was successful } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSUInteger userId = 221809; NSTimeInterval timeout = 20.0; [QBChat.instance pingUserWithID:userId timeout:timeout completion:^(NSTimeInterval timeInterval, BOOL success) { // time interval of ping and whether it was successful }]; ``` </Tab> </Tabs> SwiftObjective-C | Argument | Required | Description | | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | userId | yes | ID of the user. | | timeout | no | Ping timeout. To control how much time it takes to respond to a ping, you should set a ping timeout. If the response isn't received within the specified time frame, then the error callback is called. Default: **30** seconds. | As a result, a completion block with `timeInterval` and `success` arguments is called. | Argument | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | timeInterval | A double parameter. Indicates how long it took to ping in seconds. | | success | A boolean parameter. Indicates whether the ping was successful. If the sucess=1, the ping is successful (the user is connected to the Chat). If sucess=0, the ping isn't successful. | ## Ping server QuickBlox SDK can send application-level pings to a server. As a result, you can check if there is a connection with the Chat server. <Tabs> <Tab title="Swift"> ```Swift theme={null} let timeout = 20.0 QBChat.instance.pingServer(withTimeout: timeout) { (timeInterval, success) in // time interval of ping and whether it was successful } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSTimeInterval timeout = 20.0; [QBChat.instance pingServerWithTimeout:timeout completion:^(NSTimeInterval timeInterval, BOOL success) { // time interval of ping and whether it was successful }]; ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | timeout | no | Ping timeout. To control how much time it takes to respond to a ping, you should set a ping timeout. If the response wasn't received within the specified time frame, then the error callback is called. Default: **30** seconds. | As a result, a completion block with `timeInterval` and `success` arguments is called. | Argument | Description | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | | timeInterval | A double parameter. Indicates how long it took to ping in seconds. | | success | A boolean parameter. Indicates whether the ping was successful. If the sucess=1, the ping was successful. If sucess=0, the ping wasn't successful. | # Content Source: https://docs.quickblox.com/sdks/ios-content Learn how to store and access files with QuickBlox file storage. The content module allows storing rich chat attachments, app content, and settings without having to republish them. Using a web interface you or your clients can control and make instant changes to the apps. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. ## Retrieve files Get a list of files for a current user using the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let page = QBGeneralResponsePage(currentPage: 1, perPage: 20) QBRequest.blobs(for: page, successBlock: { (response, page, blobs) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBGeneralResponsePage *page = [QBGeneralResponsePage responsePageWithCurrentPage:1 perPage:20]; [QBRequest blobsForPage:page successBlock:^(QBResponse *response, QBGeneralResponsePage *page, NSArray *blobs) { } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> ## Upload file Upload a file to the cloud using the following code snippet. <Tabs> <Tab title="Swift"> ```Swift theme={null} // your file - this is an image in our case guard let imageData = myImage?.pngData() else { return } QBRequest.tUploadFile(imageData, fileName: "myImage", contentType: "image/png", isPublic: true, successBlock: { (response, uploadedBlob) in }, statusBlock: { (request, status) in // Update UI with upload progress }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // your file - this is an image in our case NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed:@"arrow.png"]); [QBRequest TUploadFile:imageData fileName:@"arrow.png" contentType:@"image/png" isPublic:NO successBlock:^(QBResponse *response, QBCBlob *blob) { } statusBlock:^(QBRequest *request, QBRequestStatus *status) { // Update UI with upload progress } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> The maximum size of the uploaded file depends on the membership plan. | Basic | Startup | Growth | HIPAA | Enterprise | | | --------------- | ------- | ------ | ----- | ---------- | --------------------------------------------------------------- | | File size limit | 10 Mb | 25 Mb | 50Mb | 50Mb | [Contact our sales team](https://quickblox.com/enterprise/#get) | ## Update file Update a previously uploaded file in the cloud using the `tUpdateFile(with:file:)` method below. Set the `isNew` field as `true` if you want to update a file. <Tabs> <Tab title="Swift"> ```Swift theme={null} // your new file - this is an image in our case guard let newImageData = myNewImage?.pngData() else { return } let existingBlob: QBCBlob = QBCBlob() // previously received Blob existingBlob.isNew = true // set as true if you want to update blob's file. QBRequest.tUpdateFile(with: newImageData, file: existingBlob, successBlock: { (response) in }, statusBlock: { (request, status) in // update UI with upload progress }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // your new file - this is an image in our case NSData *newImageData = UIImagePNGRepresentation([UIImage imageNamed:@"arrow.png"]); QBCBlob * existingBlob = [[QBCBlob alloc] init]; // previously received Blob existingBlob.isNew = YES; // set as YES if you want to update blob's file [QBRequest TUpdateFileWithData:newImageData file:existingBlob successBlock:^(QBResponse * _Nonnull response) { } statusBlock:^(QBRequest *request, QBRequestStatus *status) { // update UI with upload progress } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | ------------ | -------- | --------------------------------------------------------------- | | newImageData | Yes | A new file in NSData format. | | existingBlob | Yes | A previously obtained blob witha file that needs to be updated. | ## Download file by UID If the file is public then it's possible to download it without a session token. <Tabs> <Tab title="Swift"> ```Swift theme={null} let uid = "d816966db53640e68b304a3cd4e5c0c100" QBRequest.downloadFile(withUID: uid, successBlock: { (response, fileData) in }, statusBlock: { (request, status) in let progress = CGFloat(status.percentOfCompletion) }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *uid = @"d816966db53640e68b304a3cd4e5c0c100"; [QBRequest downloadFileWithUID:uid successBlock:^(QBResponse *response, NSData *fileData) { } statusBlock:^(QBRequest *request, QBRequestStatus *status) { NSLog(@"download progress: %f", status.percentOfCompletion); } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> ## Get file info Get information about a file by ID using the method below. This method allows to load the file description model from the server, not the content of the file itself. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.blob(withID: 1441441, successBlock: { (response, blob) in // content type in mime format. let blobContentType = blob.contentType // file name. let blobName = blob.name; // status of the File. let blobStatus = blob.status; // date when the file upload has been completed. let blobCompletedAt = blob.completedAt; // the size of file in bytes, readonly let blobSize = blob.size; // file unique identifier. let blobUID = blob.uid; // last read file time. let blobLastReadAccessTs = blob.lastReadAccessTs; // an instance of BlobObjectAccess. let blobObjectAccess = blob.blobObjectAccess; // coma separated string with file's tags. let blobTags = blob.tags; // file's visibility. let blobIsPublic = blob.isPublic; // set as YES if you want to update blob's file. let blobIsNew = blob.isNew; }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest blobWithID:1441441 successBlock:^(QBResponse * _Nonnull response, QBCBlob * _Nonnull blob) { // content type in mime format. NSString *blobContentType = blob.contentType; // file name. NSString *blobName = blob.name; // status of the File. QBCBlobStatus blobStatus = blob.status; // date when the file upload has been completed. NSDate *blobCompletedAt = blob.completedAt; // the size of file in bytes, readonly NSUInteger blobSize = blob.size; // file unique identifier. NSString *blobUID = blob.UID; // last read file time. NSDate *blobLastReadAccessTs = blob.lastReadAccessTs; // an instance of BlobObjectAccess. QBCBlobObjectAccess *blobObjectAccess = blob.blobObjectAccess; // coma separated string with file's tags. NSString *blobTags = blob.tags; // file's visibility. BOOL blobIsPublic = blob.isPublic; // set as YES if you want to update blob's file. BOOL blobIsNew = blob.isNew; } errorBlock:^(QBResponse *response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | fileId | yes | ID of the file/blob. | | successBlock | yes | Specifies a response block that is called if the request is succeeded. As a result, the response will contain a blob/file description model. | | errorBlock | yes | Specifies a response block that is called in case an error is occurred. | ## Get file URL There are two types of file URLs that can be obtained: private and public. * **Public URL** allows anyone to access the file, no authorization token is needed. * **Private URL** can be accessed only by QuickBlox user with a session token. ### Get public URL To receive a public URL, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let file: QBCBlob = ... let publicUrl = file.publicUrl() // or if you have only file UID let fileUID = "6221dd49a1bb46cfb61efe62c4526bd800" let publicUrl = QBCBlob.publicUrl(forFileUID: fileUID) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBCBlob *file = ...; NSString *publicUrl = [file publicUrl]; // or if you have only file UID NSString *fileUID = @"6221dd49a1bb46cfb61efe62c4526bd800"; NSString *publicUrl = [QBCBlob publicUrlForFileUID:fileUID]; ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | ----------------------------------- | | fileUID | yes | File unique identifier. Type String | ### Get private URL To get a private URL of the uploaded file, use the following code snippet. <Tabs> <Tab title="Swift"> ```Swift theme={null} let file: QBCBlob = ... let privateUrl = file.privateUrl() // or if you have only file UID let fileUID = "6221dd49a1bb46cfb61efe62c4526bd800" let privateUrl = QBCBlob.privateUrl(forFileUID: fileUID) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBCBlob *file = ...; NSString *privateUrl = [file privateUrl]; // or if you have only file UID NSString *fileUID = @"6221dd49a1bb46cfb61efe62c4526bd800"; NSString *privateUrl = [QBCBlob privateUrlForFileUID:fileUID]; ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | ----------------------------------- | | fileUID | yes | File unique identifier. Type String | ## Delete file Delete a file by file ID using the `deleteBlob()` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.deleteBlob(withID: 1441441, successBlock: { (response) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} `[QBRequest deleteBlobWithID:1441441 successBlock:^(QBResponse * _Nonnull response) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | ------------ | -------- | ----------------------------------------------------------------------- | | fileId | yes | ID of the file/blob. | | successBlock | yes | Specifies a response block that is called if the request is succeeded. | | errorBlock | yes | Specifies a response block that is called in case an error is occurred. | # Custom Objects Source: https://docs.quickblox.com/sdks/ios-custom-objects Learn how to store and sync data with QuickBlox key-value storage. Custom Objects module provides flexibility to define any data structure (schema) you need, build one-to-many relations between schemas and control permissions for all operations made on data. Schema is defined in QuickBlox Dashboard. There are two key concepts in Custom Objects: \- **Class** represents your schema and contains field names and types. \- **Record** represents the data you put into your schema. **Class** and **Record** are similar to table and row in relational database. Every class in Custom Object module comes with five mandatory predefined fields: `_id`, `user_id`, `parent_id`, `created_at`, and `updated_at`. Allowed data types: **Integer** (or Array of Integer); **String** (or Array of String); **Float** (or Array of Float); **Boolean** (or Array of Boolean); **Location** (Array of \[\< longitude >, \< latitude >]); **File**; **Date**. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. ## Create class To start using Custom Objects module, create a class: 1. Go to [QuickBlox Dashboard](https://admin.quickblox.com/signin). 2. Follow **Custom => Add => Add new class** direction. As a result, **Add new class** popup will appear. 3. Enter a class name, add any fields you want. <Frame> <img alt="ios-custom-add-filed.png" /> </Frame> 4. Click **Create class button** to create a new class. <Frame> <img alt="ios-custom-fields.png" /> </Frame> ## Create records The easiest way to create a new record from the [QuickBlox Dashboard](https://admin.quickblox.com/signin), do the following: 1. Follow **Custom => Current class => Your Class** direction. 2. Click **Add => Add record** button and **Add new record** popup will appear. 3. Fill in any fields you want. 4. Click **Add record button** and a new record will be added and shown in the table. To create a single object use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let object = QBCOCustomObject() object.className = "Movie" // your Class name // Object fields object.fields["name"] = "Star Wars" object.fields["rating"] = 9.1 object.fields["documentary"] = "false" object.fields["genre"] = "fantasy" object.fields["descriptions"] = "Star Wars is an American epic space opera franchise consisting of a film series created by George Lucas." QBRequest.createObject(object, successBlock: { (response, customObject) in // do something when object is successfully created on a server }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBCOCustomObject *object = [QBCOCustomObject customObject]; object.className = @"Movie"; // your Class name // Object fields [object.fields setObject:@"Star Wars" forKey:@"name"]; [object.fields setObject:@9.1f forKey:@"rating"]; [object.fields setObject:@NO forKey:@"documentary"]; [object.fields setObject:@"fantasy" forKey:@"genre"]; [object.fields setObject:@"Star Wars is an American epic space opera franchise consisting of a film series created by George Lucas." forKey:@"descriptions"]; [QBRequest createObject:object successBlock:^(QBResponse *response, QBCOCustomObject *object) { // do something when object is successfully created on a server } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> To create multiple objects, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let object1 = QBCOCustomObject() object1.fields["name"] = "The Avengers" object1.fields["rating"] = 10.8 object1.fields["documentary"] = "false" object1.fields["genre"] = "fantasy" object1.fields["descriptions"] = "The Avengers is an American epic space opera franchise." let object2 = QBCOCustomObject() object2.fields["name"] = "Guardians of the Galaxy" object2.fields["rating"] = 12 object2.fields["documentary"] = "false" object2.fields["genre"] = "fantasy" object2.fields["descriptions"] = "Guardians of the Galaxy is an American epic space opera franchise." QBRequest.createObjects([object1, object2], className: "Movie", successBlock: { (response, customObjects) in // response processing }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBCOCustomObject *object1 = [QBCOCustomObject customObject]; [object1.fields setObject:@"The Avengers" forKey:@"name"]; [object1.fields setObject:@10.8f forKey:@"rating"]; [object1.fields setObject:@NO forKey:@"documentary"]; [object1.fields setObject:@"fantasy" forKey:@"genre"]; [object1.fields setObject:@"The Avengers is an American epic space opera franchise." forKey:@"descriptions"]; QBCOCustomObject *object2 = [QBCOCustomObject customObject]; [object2.fields setObject:@"Guardians of the Galaxy" forKey:@"name"]; [object2.fields setObject:@12.0f forKey:@"rating"]; [object2.fields setObject:@NO forKey:@"documentary"]; [object2.fields setObject:@"fantasy" forKey:@"genre"]; [object2.fields setObject:@"Guardians of the Galaxy is an American epic space opera franchise." forKey:@"descriptions"]; [QBRequest createObjects:@[object1, object2] className:@"Movie" successBlock:^(QBResponse *response, NSArray *objects) { // response processing } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> ## Retrieve records by IDs To get records with a particular record ID, use the `objects(withClassName:ids:)` method. Go over [Sort operatos](/sdks/ios-custom-objects#sort-operators) and [Search operators](/sdks/ios-custom-objects#search-operators) sections to learn about filters and search operators you can use to retrieve records.elow. <Tabs> <Tab title="Swift"> ```Swift theme={null} let className = "Movie" let ids = ["611fc6aece9db8005e270e1c", "61239cfbce9db8003c271452", "61239cfbce9db8003c271453"] QBRequest.objects(withClassName: className, ids: ids, successBlock: { (response, objects) in // response processing }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *className = @"Movie"; NSArray *ids = @[@"611fc6aece9db8005e270e1c", @"61239cfbce9db8003c271452", @"61239cfbce9db8003c271453"]; [QBRequest objectsWithClassName:className IDs:ids successBlock:^(QBResponse *response, NSArray *objects) { // response processing } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> | Parameters | Descriptions | | ---------- | ------------------------------ | | className | Name of a custom object class. | | ids | Custom objects IDs. | ## Retrieve records You can search for records of a particular class. The request below will return all records of the `Movie` class with the `rating` value greater than `5.5`, sorted in ascending order, and limited to 5 records on the page. <Tabs> <Tab title="Swift"> ```Swift theme={null} let className = "Movie" var extendedRequest:[String: String] = [:] extendedRequest["rating[gt]"] = "5.5" extendedRequest["limit"] = "5" extendedRequest["documentary"] = "false" extendedRequest["sort_asc"] = "rating" QBRequest.objects(withClassName: className, extendedRequest: (extendedRequest as NSDictionary).mutableCopy() as? NSMutableDictionary, successBlock: { (response, customObjects, page) in // response processing }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *className = @"Movie"; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; [extendedRequest setObject:@"5.5" forKey:@"rating[gt]"]; [extendedRequest setObject:@"5" forKey:@"limit"]; [extendedRequest setObject:[NSNumber numberWithBool:NO] forKey:@"documentary"]; [extendedRequest setObject:@"rating" forKey:@"sort_asc"]; [QBRequest objectsWithClassName:className extendedRequest:extendedRequest successBlock:^(QBResponse *response, NSArray *objects, QBResponsePage *page) { // response processing } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> | Argument | Required | Description | | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | className | yes | Name of a custom object class. | | extendedRequest | yes | A dictionary that stores keys and values of the String type. The keys are formed as parameters of the [List Records](https://docs.quickblox.com/reference/list-records) request. | If you want to retrieve only records updated after some specific date time and order the search results, you can apply operators. Thus, you can apply [search](/sdks/ios-custom-objects#search-operators) and [sort](/sdks/ios-custom-objects#sort-operators) operators to list records on the page so that it is easier to view specific records. The operators are set as key-value parameters in the `extendedRequest` dictionary. If you want to get a paginated list of records from the server, you can set the following pagination parameters in the `extendedRequest` dictionary. | Pagination parameter | Required | Description | | -------------------- | -------- | ------------------------------------------------------------------------------------------- | | skip | no | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | no | Limit search results to N records. Useful for pagination. Default value: **100**. | ### Search operators You can use search operators to get more specific search results. The request below will return records of the `Movie` class by the `rating` field with a value greater than `5.5`. <Tabs> <Tab title="Swift"> ```Swift theme={null} let className = "Movie" var extendedRequest:[String: String] = [:] extendedRequest["rating[gt]"] = "5.5" QBRequest.objects(withClassName: className, extendedRequest: (extendedRequest as NSDictionary).mutableCopy() as? NSMutableDictionary, successBlock: { (response, customObjects, page) in // response processing }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *className = @"Movie"; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; [extendedRequest setObject:@"5.5" forKey:@"rating[gt]"]; [QBRequest objectsWithClassName:className extendedRequest:extendedRequest successBlock:^(QBResponse *response, NSArray *objects, QBResponsePage *page) { // response processing } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Description | | ---------------- | ------------------------------- | ---------------------------------------------------- | | lt | integer, float | **Less Than** operator. | | lte | integer, float | **Less Than** or **Equal** to operator. | | gt | integer, float | **Greater Than** operator. | | gte | integer, float | **Greater Than** or **Equal** to operator. | | ne | integer, float, string, boolean | **Not Equal** to operator. | | in | integer, float, string | **IN** array operator. | | nin | integer, float, string | Not **IN** array operator. | | all | array | **ALL** are contained in array. | | or | integer, foat, string | All records that contain a value 1 **or** value 2. | | ctn | string | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return records of the `Movie` class by the `rating` field sorted in ascending order. <Tabs> <Tab title="Swift"> ```Swift theme={null} let className = "Movie" var extendedRequest:[String: String] = [:] extendedRequest["sort_asc"] = "rating" QBRequest.objects(withClassName: className, extendedRequest: (extendedRequest as NSDictionary).mutableCopy() as? NSMutableDictionary, successBlock: { (response, customObjects, page) in // response processing }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *className = @"Movie"; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; [extendedRequest setObject:@"rating" forKey:@"sort_asc"]; [QBRequest objectsWithClassName:className extendedRequest:extendedRequest successBlock:^(QBResponse *response, NSArray *objects, QBResponsePage *page) { // response processing } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> Here are the sort operators that you can use to order the search results | Sort operator | Applicable to types | Description | | ------------- | ------------------- | ------------------------------------------------------------------------- | | sort\_asc | All types | Search results will be sorted in ascending order by the specified field. | | sort\_desc | All types | Search results will be sorted in descending order by the specified field. | ## Get number of records You can get a number of records using the `countObjects(withClassName:extendedRequest:)` method. The request below will return a count of records of the Movie class with the `documentary` field set to `false`. <Tabs> <Tab title="Swift"> ```Swift theme={null} let className = "Movie" var extendedRequest:[String: String] = [:] extendedRequest["documentary"] = "false" QBRequest.countObjects(withClassName: className, extendedRequest: (extendedRequest as NSDictionary).mutableCopy() as? NSMutableDictionary, successBlock: { (response, count) in // response processing }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *className = @"Movie"; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; [extendedRequest setObject:@NO forKey:@"documentary"]; [QBRequest countObjectsWithClassName:className extendedRequest:extendedRequest successBlock:^(QBResponse * _Nonnull response, NSUInteger count) { // response processing } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> Here are the aggregation operators you can use to retrieve records. | Aggregation operator | Required | Description | | -------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | className | yes | A name of the custom object class. | | extendedRequest | yes | A dictionary that stores keys and values of the String type. The keys are formed as parameters of the [List Records](https://docs.quickblox.com/reference/list-records) request. | ## Update records You can update a single record using the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let object = QBCOCustomObject() object.className = "Movie" object.fields.setObject("7.88", forKey: NSString(string: "rating")) object.id = "502f7c4036c9ae2163000002" QBRequest.update(object, successBlock: { (response, customObject) in // object updated }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBCOCustomObject *object = [QBCOCustomObject customObject]; object.className = @"Movie"; [object.fields setObject:@"7.88" forKey:@"rating"]; object.ID = @"502f7c4036c9ae2163000002"; [QBRequest updateObject:object successBlock:^(QBResponse *response, QBCOCustomObject *object) { // object updated } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> You can update multiple records using the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let object1 = QBCOCustomObject() object1.fields.setObject("101", forKey: NSString(string: "rating")) object1.id = "5228ad042195be5d8d41bd99" let object2 = QBCOCustomObject() object2.fields.setObject("201", forKey: NSString(string: "rating")) object2.id = "5228ad042195be5d8d41bd9a" let object3 = QBCOCustomObject() object3.fields.setObject("31", forKey: NSString(string: "rating")) object3.id = "5228ad042195be5d8d41bd9a33" QBRequest.update([object1, object2, object3], className: "SuperSample", successBlock: { (response, customObjects, notFoundObjectsIds) in // response processing }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBCOCustomObject *object1 = [QBCOCustomObject customObject]; object1.ID = @"5228ad042195be5d8d41bd99"; [object1.fields setObject:@"101" forKey:@"rating"]; // QBCOCustomObject *object2 = [QBCOCustomObject customObject]; object2.ID = @"5228ad042195be5d8d41bd9a"; [object2.fields setObject:@"201" forKey:@"rating"]; // QBCOCustomObject *object3 = [QBCOCustomObject customObject]; object3.ID = @"5228ad042195be5d8d41bd9a33"; [object3.fields setObject:@"31" forKey:@"rating"]; [QBRequest updateObjects:@[object1, object2, object3] className:@"SuperSample" successBlock:^(QBResponse *response, NSArray *objects, NSArray *notFoundObjectsIds) { // response processing } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> ## Delete records To delete a single record, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let iD = "502f83ed36c9aefa62000002" let className = "Movie" QBRequest.deleteObject(withID: iD, className: className, successBlock: { (response) in // object deleted }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *iD = @"502f83ed36c9aefa62000002"; NSString *className = @"Movie"; [QBRequest deleteObjectWithID:iD className:className successBlock:^(QBResponse *response) { // object deleted } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> To delete multiple records, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} let ids = ["51c9aafe535c127d98004a15", "51c9ab92535c12951b0032d6", "51c9ab92535c12951b0032da", "51c9ab92535c12951b0032de", "52283b38535c12fa32010efd"] let className = "Movie" QBRequest.deleteObjects(withIDs: ids, className: className, successBlock: { (response, deletedObjectsIDs, notFoundObjectsIDs, wrongPermissionsObjectsIDs) in // response processing }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSArray *ids = @[@"51c9aafe535c127d98004a15", @"51c9ab92535c12951b0032d6", @"51c9ab92535c12951b0032da", @"51c9ab92535c12951b0032de", @"52283b38535c12fa32010efd"]; NSString *className = @"Movie"; [QBRequest deleteObjectsWithIDs:ids className:className successBlock:^(QBResponse *response, NSArray *deletedObjectsIDs, NSArray *notFoundObjectsIDs, NSArray *wrongPermissionsObjectsIDs) { // response processing } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> ## Relations It is possible to create a relation between objects of **two different classes** via `_parent_id` field. For example, we have the class **Rating** that contains `score`, `review`, and `comment` fields. We also have a **Movie** class. So we can create a record of class **Rating** that will point to the record of the class **Movie** via its `_parent_id` field, so the `_parent_id` field will contain the ID of record from class **Movie**. <Warning> This is not a simple soft link. This is actually a **hard link**. When you delete the **Movie** class record then all its children (records of class **Rating** with `_parent_id` field set to the **Movie** class record ID) will be automatically **deleted** as well. </Warning> <Note> If you need to retrieve all children, you can retrieve records with the filter `_parent_id=<id_of_parent_class_record>`. </Note> ## Permissions <Note> Access Control list available **only** for Custom Objects module. </Note> Access control list (ACL) is a list of permissions attached to some object. An ACL specifies which users have access to objects as well as what operations are allowed on given objects. Each entry in a typical ACL specifies a subject and an operation. ACL models may be applied to collections of objects as well as to individual entities within the system hierarchy. ### Permission schema QuickBlox Permission schema contains **five** permissions levels: * **Open (open)** Such permission schema means that any user within the application can access the record/records in the class and is allowed to perform an action with the current permission level. * **Owner (owner)** Owner permission level means that only Owner (a user who created a record) is allowed to perform action with the current permission level. * **Not allowed (not\_allowed)** No one (except for the Account Administrator) can make a chosen action. * **Open for groups (open\_for\_groups)** Users having a specified tag/tags (see more info about how to set tags for the user in [Users](/sdks/ios-users) section) will be included in the group that is allowed to perform an action with the current permission level. The current permission level can consist of one or several groups (number of groups is **not** limited). Tags can be added/deleted in the user’s profile. * **Open for user ids (open\_for\_users\_ids)** Only users that are specified in the permission level can make a required action with a record. One or several users can be specified (the number of users is **not** limited). ### Actions available for the entity * **Create** Create a record. * **Read** Retrieve and read the info about the chosen record. * **Update** Update any parameter for the chosen record (only those parameters that can be set by the user can be updated). * **Delete** Delete a record. ### Permission levels There are two access levels in the Permissions schema: **Class** and **Record**. #### Class entity Only the Account Administrator can create a class in the Custom object module and make all possible actions with it. Operations with **Class** entity are not allowed in API. All actions (Create, Read, Update, and Delete) are available for the class entity and are applicable for all records in the class. Every action has a separate permission level available. The exception is a Create action that is not available for the **Owner** permission level. To set a permission schema for the Class, do the following: 1. Go to the **Custom Objects** tab. 2. Open a required class. 3. Click **Edit permissions** button to open a class and edit it. <img alt="" /> Default Class permission schema is used while creating a class: * **Create**: Open * **Read**: Open * **Update**: Owner * **Delete**: Owner <Note> Mark checkboxes to **enable** class permissions. </Note> #### Record entity A record is an entity within the class in the Custom Objects module that has its own permission levels. You can create a record in the Dashboard and API (see [Create Record](#section-create-records) request for more details). All permission levels except for the **Not Allowed** are available for the record and there are only three actions available and applicable for the record: Read, Update, and Delete. Default Record permission schema is used while creating a class: * **Read**: Open * **Update**: Owner * **Delete**: Owner To set a permission level open the required Class and click the record to edit it. <Frame> <img alt="ios-custom-edit-record.png" /> </Frame> ### Choosing a permission schema Only **one** permission level can be applicable to the record: class permission schema or record permission schema. To apply class permission levels to **all** records in the class tick the checkbox in the **Use Class permissions** column near the required Action in the Dashboard. <Frame> <img alt="ios-custom-edit-permissions.png" /> </Frame> <Note> Using a class permission schema means that a record permission schema will **not** affect a reсord. </Note> <Tip> In case, the Admin **does not** tick the checkbox in the Dashboard a user **has** a possibility to change permission levels for every separate record in the table or create a new one with the ACL that a user requires. </Tip> ### Create record with permissions Let's create a record with the next permissions: * READ: Open. * UPDATE: Users in groups **golf**, **man**. * DELETE: Users with IDs **3060**, **63635**. <Tabs> <Tab title="Swift"> ```Swift theme={null} let object = QBCOCustomObject() object.className = "Movie" // your Class name // Object fields object.fields.setObject("Star Wars", forKey: NSString(string: "name")) object.fields.setObject("fantasy", forKey: NSString(string: "genre")) // permissions let permissions = QBCOPermissions() // READ // permissions.readAccess = QBCOPermissionsAccessOpen // UPDATE // permissions.updateAccess = QBCOPermissionsAccessOpenForGroups permissions.usersGroupsForUpdateAccess = ["golf", "man"] // DELETE // permissions.deleteAccess = QBCOPermissionsAccessOpenForUsersIDs; permissions.usersIDsForDeleteAccess = [3060, 63635] object.permissions = permissions QBRequest.createObject(object, successBlock: { (response, customObject) in // do something when object is successfully created on a server }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBCOCustomObject *object = [QBCOCustomObject customObject]; object.className = @"Movie"; // your Class name // Object fields [object.fields setObject:@"Star Wars" forKey:@"name"]; [object.fields setObject:@"fantasy" forKey:@"genre"]; // permissions QBCOPermissions *permissions = [QBCOPermissions permissions]; // READ // permissions.readAccess = QBCOPermissionsAccessOpen; // UPDATE // permissions.updateAccess = QBCOPermissionsAccessOpenForGroups; permissions.usersGroupsForUpdateAccess = @[@"golf", @"man"]; // DELETE // permissions.deleteAccess = QBCOPermissionsAccessOpenForUsersIDs; permissions.usersIDsForDeleteAccess = @[@3060, @63635]; object.permissions = permissions; [QBRequest createObject:object successBlock:^(QBResponse *response, QBCOCustomObject *object) { // do something when object is successfully created on a server } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> ### Retrieve record permissions You can obtain info about record permissions by its ID. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.permissionsForObject(withClassName: "SuperSample", id: "51c9aafe535c127d98004a13", successBlock: { (response, permissions) in // response processing }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest permissionsForObjectWithClassName:@"SuperSample" ID:@"51c9aafe535c127d98004a13" successBlock:^(QBResponse *response, QBCOPermissions *permissions) { // response processing } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> <Warning> **Only** info about the user's own records is available. </Warning> ### Update record permissions Let's update record's permissions to next: * READ: Users in groups **car, developers**. * UPDATE: Owner. * DELETE: Owner. <Tabs> <Tab title="Swift"> ```Swift theme={null} let object = QBCOCustomObject() object.className = "Movie" // your Class name // permissions let permissions = QBCOPermissions() // READ // permissions.readAccess = QBCOPermissionsAccessOpenForGroups permissions.usersGroupsForUpdateAccess = ["car", "developers"] // UPDATE // permissions.updateAccess = QBCOPermissionsAccessOwner // DELETE // permissions.deleteAccess = QBCOPermissionsAccessOwner; object.permissions = permissions QBRequest.update(object, specialUpdateOperators: [:], successBlock: { (response, customObject) in // object updated }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBCOCustomObject *object = [QBCOCustomObject customObject]; object.className = @"Movie"; // your Class name // permissions QBCOPermissions *permissions = [QBCOPermissions permissions]; // READ // permissions.readAccess = QBCOPermissionsAccessOpenForGroups; permissions.usersGroupsForUpdateAccess = @[@"car", @"developers"]; // UPDATE // permissions.updateAccess = QBCOPermissionsAccessOwner; // DELETE // permissions.deleteAccess = QBCOPermissionsAccessOwner; object.permissions = permissions; [QBRequest updateObject:object specialUpdateOperators:nil successBlock:^(QBResponse *response, QBCOCustomObject *object) { // object updated } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> ## Files Custom Objects module supports the `File` field type. It is created to work easily with content from the Custom Objects module. There is an ability to upload, download, update and delete the content of file fields. ## Upload/Update file Use the code lines below to upload/update a file. <Tabs> <Tab title="Swift"> ```Swift theme={null} let file = QBCOFile() file.name = "plus" file.contentType = "image/png" let imagePlus = UIImage(named: "plus") guard let imageData = imagePlus?.pngData() else { return } file.data = imageData QBRequest.uploadFile(file, className: "Movie", objectID: "5256c265535c128020000182", fileFieldName: "imagePlus", successBlock: { (response, info) in // uploaded }, statusBlock: { (request, status) in // handle progress }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBCOFile *file = [QBCOFile file]; file.name = @"plus"; file.contentType = @"image/png"; file.data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"plus" ofType:@"png"]]; [QBRequest uploadFile:file className:@"Movie" objectID:@"5256c265535c128020000182" fileFieldName:@"image" successBlock:^(QBResponse *response, QBCOFileUploadInfo *info) { // uploaded } statusBlock:^(QBRequest *request, QBRequestStatus *status) { // handle progress } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> ## Download file To download a file, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.downloadFile(fromClassName: "Movie", objectID: "5256c265535c128020000182", fileFieldName: "imagePlus", successBlock: { (response, data) in // file downloaded }, statusBlock: { (request, status) in // handle progress }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest downloadFileFromClassName:@"Movie" objectID:@"5256c265535c128020000182" fileFieldName:@"image" successBlock:^(QBResponse *response, NSData *loadedData) { // file downloaded } statusBlock:^(QBRequest *request, QBRequestStatus *status) { // handle progress } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> ## Delete file To delete a file, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.deleteFile(fromClassName: "Movie", objectID: "5256c265535c128020000182", fileFieldName: "imagePlus", successBlock: { (response) in // file deleted }, errorBlock: { (response) in // error handling debugPrint("error: \(response.error?.error?.localizedDescription)") }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest deleteFileFromClassName:@"Movie" objectID:@"5256c265535c128020000182" fileFieldName:@"image" successBlock:^(QBResponse *response) { // file deleted } errorBlock:^(QBResponse *response) { // error handling NSLog(@"Response error: %@", [response.error.error description]); }]; ``` </Tab> </Tabs> # Push Notifications Source: https://docs.quickblox.com/sdks/ios-push-notifications Learn how to send push notifications to users when they are offline. Push Notifications provide a way to deliver some information to a user while they are not using your app actively. The following use cases can be covered by push notifications: * **Offline messages.** Send a chat message when a recipient is offline. In this case, a push notification will be sent automatically if the user is offline. * **Offline calls.** Make a video/audio call with offline opponents. In this case, a push notification will be sent manually. * **Requests to contact list.** Send requests to add a user to the contact list. In this case, a push notification will be sent manually). * **User tags.** Send notifications to specific user groups defined by tags. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. ## Create APNs certificate ### Create App ID Each iOS application that uses the APNs must have a unique application ID that uniquely identifies your app. The following steps describe how to create an Apple App ID for your application. If you already have an App ID, you can skip these steps. 1. Once you have signed in to the [iPhone Developer Connection Portal](https://developer.apple.com/account/), click on **Certificates, IDs & Profiles.** <Frame> <img alt="app id.png" /> </Frame> 2. On the next screen, select **Identifiers => App IDs** in the left sidebar and then click on the plus button in the top right corner. <Frame> <img alt="identifiers=>app ids.png" /> </Frame> 3. Enter your app name for the **App ID Description** section. In the **App ID Suffix** section, select the **Explicit App ID** option and enter **Bundle Identifier** in the corresponding field. <Frame> <img alt="bundle ID.png" /> </Frame> <Tip> Ensure you have created an **App ID** without a wildcard. Make sure it doesn’t have an asterisk in it. **Wildcard IDs** cannot use the push notification service. </Tip> 4. Locate **App services section** and check **Push Notifications** as the service you would like to add in your app. 5. Once you have entered all information, scroll down to the bottom of the page and select **Continue** button. <Frame> <img alt="push noticaitons.png" /> </Frame> 6. You will now be presented with an overview of your new app ID. Click **Register** button to continue. <Frame> <img alt="register.png" /> </Frame> ### Generate a Certificate Signing Request You need to generate a certificate signing request file so that you can use it to request an APNs SSL certificate later on. Follow the instructions below to create one: 1. Launch the **Keychain Access** application in your macOS and select **Keychain Access => Certificate Assistant => Request a Certificate From a Certificate Authority**. <Frame> <img alt="keychain.png" /> </Frame> 2. Enter the required information and check the **Saved to disk** option. Click **Continue**. <Frame> <img alt="certificate info.png" /> </Frame> 3. Save the certificate request using the suggested name and click **Save**. <Frame> <img alt="save request.png" /> </Frame> ### Create universal APNs certificate 1. On the **Certificates, IDs & Profiles** screen locate **Certificates => All** section in the left sidebar and then click on the plus button in the top right corner of the screen. <Frame> <img alt="certicates => all.png" /> </Frame> 2. The **Add iOS Certificate** screen will appear. Choose **Apple Push Notifications service SSL (Sandbox & Production)** option and click **Continue**: <Frame> <img alt="add certificate screen.png" /> </Frame> Here we will create a single **universal** push certificate that is good for both Development and Production environments. 3. On the next screen choose an App ID you want to create a push certificate for. Then click **Continue**. <Frame> <img alt="choose app id.png" /> </Frame> 4. Locate **About Creating a Certificate Signing Request (CSR)** and click **Continue**: <Frame> <img alt="about creating.png" /> </Frame> 5. Click **Choose File…** button to locate **Certificate Request File** that you have saved earlier. Click **Continue**. <Frame> <img alt="choose file.png" /> </Frame> 6. Your APNs SSL Certificate will now be generated. Click **Download** and save it to your hard disk. <Frame> <img alt="download certificate.png" /> </Frame> You can also download your new certificate on App ID details page. <Frame> <img alt="push notifications done.png" /> </Frame> ### Create p12 file 1. Once your certificate has been created, open the file with **Keychain Access** app on your computer. If you are asked which keychain to add the certificate to, select **login** option. SSL certificate will be used by provider(QuickBlox) application so that it can contact the APNs to send push notifications to your applications. 2. Still in **Keychain Access** app, select **Certificates** option in the bottom section of the left sidebar. You will now need to find the certificate you have created. It will have the name **Apple Push Services: \< Bundle ID >** and paired with a private key. 3. Right-click on this key and select **Export "Apple Push Services …"** option. <Frame> <img alt="export .png" /> </Frame> In the popup that appears, you can call the file whatever you want, but make sure that **File Format** is set to **Personal Information Exchange (.p12).** When exporting the certificate, you can provide a password for the **p12** file. That's all. Now you have a ready-to-go APNs certificate that can be used on both the Development and Production environments of your iOS app. ### Upload certificate to Dashboard To upload the APNs certificate to QuickBlox Dashboard you need to do the following: 1. Open your QuickBlox [Dashboard](https://admin.quickblox.com/signin). 2. Go to **Push notifications** module section. 3. Switch to the **Settings** tab. 4. Locate **Apple Push Notifications Service (APNs)** section. 5. Upload the newly created APNs certificate. <Frame> <img alt="ios-push-settings.png" /> </Frame> Choose the certificate and enter the password if needed. After this, you will be able to start using push notifications in your iOS application. ## Subscribe To start receiving push notifications, you need to get the current device push token and subscribe with it on the backend as follows. A single user can have up to **10** subscriptions on different devices. <Tabs> <Tab title="Swift"> ```Swift theme={null} class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { UIApplication.shared.registerForRemoteNotifications() return true } ... //MARK: - UNUserNotification func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { guard let identifierForVendor = UIDevice.current.identifierForVendor else { return } let deviceIdentifier = identifierForVendor.uuidString let subscription = QBMSubscription() subscription.notificationChannel = .APNS subscription.deviceUDID = deviceIdentifier subscription.deviceToken = deviceToken QBRequest.createSubscription(subscription, successBlock: { (response, objects) in }, errorBlock: { (response) in debugPrint("[AppDelegate] createSubscription error: \(String(describing: response.error))") }) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { debugPrint("Unable to register for remote notifications: \(error.localizedDescription)") } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[UIApplication sharedApplication] registerForRemoteNotifications]; return YES; } ... - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSString *deviceIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; // subscribing for push notifications QBMSubscription *subscription = [QBMSubscription subscription]; subscription.notificationChannel = QBMNotificationChannelAPNS; subscription.deviceUDID = deviceIdentifier; subscription.deviceToken = deviceToken; [QBRequest createSubscription:subscription successBlock:nil errorBlock:nil]; } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { NSLog(@"Failed to register for notifications:%@", error); } @end ``` </Tab> </Tabs> <Note> You cannot test push notifications in the iOS simulator. You need a physical iOS device and an Apple developer account. </Note> <Warning> The `deviceToken` is a device registration token generated by the APNs or GCM/FCM. The token can be unregistered by the APNs or GCM/FCM anytime. In this case, the device should be registered again and obtain a new token. When a new token is obtained, a new subscription should be created. </Warning> ## Subscribe for VoIP push notifications Normally, the VoIP push notifications are used in applications with video/audio calling capabilities. In the past, a VoIP app had to maintain a persistent network connection with a server to receive incoming calls and other data. This meant writing complex code that sent periodic messages back and forth between the app and server to keep a connection alive, even when the app was not in use. This technique resulted in frequent device wakes that wasted energy. It also meant that if a user quit the VoIP app, calls from the server could no longer be received. Instead of persistent connections, developers should use VoIP push notifications. VoIP push notifications are different than regular APNs notifications mainly in how they are set up in the iOS app. iOS SDK provides the [PushKit API](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/OptimizeVoIP.html) to implement VoIP push notifications. <Tabs> <Tab title="Swift"> ```Swift theme={null} import PushKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { self.voipRegistration() return true } // Register for VoIP notifications func voipRegistration() { let mainQueue = dispatch_get_main_queue() // Create a push registry object let voipRegistry: PKPushRegistry = PKPushRegistry(mainQueue) // Set the registry's delegate to self voipRegistry.delegate = self // Set the push type to VoIP voipRegistry.desiredPushTypes = [PKPushTypeVoIP] } // MARK: - PKPushRegistryDelegate protocol func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) { // Register VoIP push token (a property of PKPushCredentials) with server let deviceIdentifier: String = UIDevice.current.identifierForVendor!.uuidString let subscription = QBMSubscription() subscription.notificationChannel = .APNSVOIP subscription.deviceUDID = deviceIdentifier subscription.deviceToken = deviceToken QBRequest.createSubscription(subscription, successBlock: { (response, objects) in }, errorBlock: { (response) in debugPrint("[AppDelegate] createSubscription error: \(String(describing: response.error))") }) } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} #import <PushKit/PushKit.h> @interface AppDelegate () <PKPushRegistryDelegate> @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [self voipRegistration]; return YES; } // Register for VoIP notifications - (void) voipRegistration { dispatch_queue_t mainQueue = dispatch_get_main_queue() // Create a push registry object PKPushRegistry * voipRegistry = [[PKPushRegistry alloc] initWithQueue: mainQueue]; // Set the registry's delegate to self voipRegistry.delegate = self; // Set the push type to VoIP voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; } // MARK: - PKPushRegistryDelegate protocol - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials: (PKPushCredentials *)credentials forType:(NSString *)type { // Register VoIP push token (a property of PKPushCredentials) with server NSString *deviceIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; // subscribing for push notifications QBMSubscription *subscription = [QBMSubscription subscription]; subscription.notificationChannel = QBMNotificationChannelAPNSVOIP; subscription.deviceUDID = deviceIdentifier; subscription.deviceToken = deviceToken; [QBRequest createSubscription:subscription successBlock:nil errorBlock:nil]; } @end ``` </Tab> </Tabs> ## Send APNs push notifications You can manually initiate sending of push notifications to a user/users on any event in your application. To do so, you need to form push notification parameters (payload) and set push recipients. <Tabs> <Tab title="Swift"> ```Swift theme={null} let event = QBMEvent() event.notificationType = .push event.usersIDs = "20,21" event.type = .oneShot var pushParameters = [String : String]() pushParameters["message"] = "Bitcoin trends" pushParameters["ios_badge"] = "2" pushParameters["ios_sound"] = "app_sound.wav" // custom params pushParameters["thread_likes"] = "24" pushParameters["thread_id"] = "678923" if let jsonData = try? JSONSerialization.data(withJSONObject: pushParameters, options: .prettyPrinted) { let jsonString = String(bytes: jsonData, encoding: String.Encoding.utf8) event.message = jsonString } QBRequest.createEvent(event, successBlock: {(response, events) in }, errorBlock: {(response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBMEvent *event = [QBMEvent event]; event.notificationType = QBMNotificationTypePush; event.usersIDs = @"21,22"; event.type = QBMEventTypeOneShot; NSMutableDictionary *pushParameters = [NSMutableDictionary dictionary]; pushParameters[@"message"] = @"Bitcoin trends"; pushParameters[@"ios_badge"] = @"2"; pushParameters[@"ios_sound"] = @"app_sound.wav"; // custom parameters pushParameters[@"thread_likes"] = @"24"; pushParameters[@"thread_id"] = @"678923"; NSError *error = nil; NSData *sendData = [NSJSONSerialization dataWithJSONObject:pushParameters options:NSJSONWritingPrettyPrinted error:&error]; NSString *jsonString = [[NSString alloc] initWithData:sendData encoding:NSUTF8StringEncoding]; event.message = jsonString; [QBRequest createEvent:event successBlock:^(QBResponse * _Nonnull response, NSArray<QBMEvent *> * _Nullable events) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | -------------------------------- | | event | Yes | A model for representing events. | <Warning> You can send only FCM data messages to the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages on your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notification. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> <Warning> If the iOS app is not subscribed to APNs VoIP notifications or the APNs VoIP certificate has expired, the regular APNs will be delivered instead of APNs VoIP. </Warning> ## Send VoIP push notifications You can manually initiate sending of push notifications to a user/users on any event in your application. To do so, you need to form push notification parameters (payload) and set push recipients. <Tabs> <Tab title="Swift"> ```Swift theme={null} let event = QBMEvent() event.notificationType = QBMNotificationType.push event.usersIDs = "20,21" event.type = QBMEventType.oneShot let payload = [ "ios_voip": "1", "VOIPCall": "1", "sessionID": "767565gfh865486h548k6586h5868", "opponentsIDs": "20,21" ] let data = try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted) var message = "" if let data = data { message = String(data: data, encoding: .utf8) ?? "" } event.message = message QBRequest.createEvent(event, successBlock: {(response, events) in }, errorBlock: {(response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBMEvent *event = [QBMEvent event]; event.notificationType = QBMNotificationTypePush; event.usersIDs = @"21,22"; event.type = QBMEventTypeOneShot; NSDictionary *payload = @{ @"ios_voip" : @"1", @"VOIPCall" : @"1", @"sessionID" : @"767565gfh865486h548k6586h5868", @"opponentsIDs" : @"20,21" }; NSData *data = [NSJSONSerialization dataWithJSONObject:payload options:NSJSONWritingPrettyPrinted error:nil]; NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; event.message = message; [QBRequest createEvent:event successBlock:^(QBResponse * _Nonnull response, NSArray<QBMEvent *> * _Nullable events) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | -------------------------------- | | event | Yes | A model for representing events. | ## Receive push notifications To receive push notifications, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} // MARK: - AppDelegate func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) { } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Swift.Void) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // MARK: - AppDelegate - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { } - (void)application:(UIApplication *)__unused application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { } ``` </Tab> </Tabs> ## Receive VoIP push notifications To receive push notifications, use the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} // MARK: - PKPushRegistryDelegate protocol // Handle incoming pushes func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // MARK: - PKPushRegistryDelegate protocol // Handle incoming pushes - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type { } ``` </Tab> </Tabs> ## Unsubscribe For a user to stop receiving push notifications, unsubscribe from receiving push notifications by invoking the `deleteSubscription(withID: subscription.id)` method below. You can also specify the device ID to verify if the subscription is being deleted for a specific device. <Tabs> <Tab title="Swift"> ```Swift theme={null} guard let uuidString = UIDevice.current.identifierForVendor?.uuidString else { return } QBRequest.subscriptions(successBlock: { (response, subscriptions) in for subscription in subscriptions { if let subscriptionsUIUD = subscription.deviceUDID, subscriptionsUIUD == uuidString { QBRequest.deleteSubscription(withID: subscription.id) { (response) in //Block with response if the request delete Subscription is succeeded. }, errorBlock: { (response) in //Block with response if the request delete Subscription is failed. }) } } }, errorBlock: {(response) in //Block with response instance if the request get subscriptions is failed. }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *deviceIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; [QBRequest subscriptionsWithSuccessBlock:^(QBResponse * _Nonnull response, NSArray<QBMSubscription *> * _Nullable objects) { for (QBMSubscription *subscription in objects) { if ([subscription.deviceUDID isEqualToString:deviceIdentifier]) { [QBRequest deleteSubscriptionWithID:subscription.ID successBlock:^(QBResponse * _Nonnull response) { //Block with response if the request delete Subscription is succeeded. } errorBlock:^(QBResponse * _Nonnull response) { //Block with response if the request delete Subscription is failed. }]; } } } errorBlock:^(QBResponse * _Nonnull response) { //Block with response instance if the request get subscriptions is failed. }]; ``` </Tab> </Tabs> | Argument | Required | Description | | --------------- | -------- | ----------------------------- | | subscription.id | yes | ID of QuickBlox subscription. | ## Unsubscribe VoIP push notifications For a user to stop receiving VoIP push notifications, unsubscribe from receiving VoIP push notifications by invoking the `deleteSubscription(withID: subscription.id)` method below. You can also specify the device ID to verify if the subscription is being deleted for a specific device. <Tabs> <Tab title="Swift"> ```Swift theme={null} // MARK: - PKPushRegistryDelegate protocol func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) { guard let uuidString = UIDevice.current.identifierForVendor?.uuidString else { return } QBRequest.subscriptions(successBlock: { (response, subscriptions) in for subscription in subscriptions { if let subscriptionsUIUD = subscription.deviceUDID, subscriptionsUIUD == uuidString, subscription.notificationChannel == .APNSVOIP { QBRequest.deleteSubscription(withID: subscription.id) { (response) in //Block with response if the request delete Subscription is succeeded. } errorBlock: { (response) in //Block with response if the request delete Subscription is failed. } } } }) { (response) in //Block with response instance if the request get subscriptions is failed. } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // MARK: - PKPushRegistryDelegate protocol - (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type { NSString *deviceIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; [QBRequest subscriptionsWithSuccessBlock:^(QBResponse * _Nonnull response, NSArray<QBMSubscription *> * _Nullable objects) { for (QBMSubscription *subscription in objects) { if ([subscription.deviceUDID isEqualToString:deviceIdentifier] && subscription.notificationChannel == QBMNotificationChannelAPNSVOIP) { [QBRequest deleteSubscriptionWithID:subscription.ID successBlock:^(QBResponse * _Nonnull response) { //Block with response if the request delete Subscription is succeeded. } errorBlock:^(QBResponse * _Nonnull response) { //Block with response if the request delete Subscription is failed. }]; } } } errorBlock:^(QBResponse * _Nonnull response) { //Block with response instance if the request get subscriptions is failed. }]; } ``` </Tab> </Tabs> | Argument | Required | Description | | --------------- | -------- | --------------------------------- | | subscription.id | yes | ID of the QuickBlox subscription. | ## Troubleshooting **A subscription is removed after a push is sent and the push isn't delivered** **Cause**: a device registration token is invalid. <Note> The device registration token is represented as `deviceToken` within the system and is set in the `subscription`. See [this section](/sdks/ios-push-notifications#subscribe) to learn how to subscribe a device to push notifications. </Note> <Tip> **Tip**: check if the device registration is correct. The device registration token can be invalid due to a number of reasons: </Tip> 1. Some other data is set instead of a correct device registration token. For example, a Firebase project ID, Firebase user token, etc. 2. The client app unregistered itself from GCM/FCM. This can happen if the user uninstalls the application or, on iOS, if the APNs Feedback Service reported the APNs token as invalid. 3. The registration token expired. For example, Google might decide to refresh registration tokens or the APNs token may have expired for iOS devices. 4. The client app was updated, but the new version is not configured to receive messages. For all these cases, remove the invalid device registration token and stop using it to send messages. Then, obtain a new token and make sure to create a new subscription with a valid token. # Quick Start Source: https://docs.quickblox.com/sdks/ios-quick-start Learn how to install QuickBlox SDK and send your first message. QuickBlox SDK helps you implement real-time chat, video chat, and push notifications to your app. You can fully concentrate on your mobile app development. QuickBlox iOS SDK supports both **Swift** and **Objective-C** programming languages. ## Start with sample apps If you are just starting your app and developing it from scratch, we recommend to use our sample apps. We use GitHub repositories to make it easy to explore, copy, and modify our code samples. The guide on how to launch and configure the sample app is on GitHub. ### Chat samples Choose the code sample below to jump-start the development. <CardGroup> <Card title="Objective-C Chat Sample App" icon="apple"> <a href="https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-chat-obj-c"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/ios-chat"> <Icon icon="book" /> Documentation </a> </Card> <Card title="Swift Chat Sample App" icon="swift"> <a href="https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-chat-swift"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/ios-chat"> <Icon icon="book" /> Documentation </a> </Card> </CardGroup> ### Video calling samples Choose the code sample below to jump-start the development. <CardGroup> <Card title="Objective-C Video Calling Sample App" icon="apple"> <a href="https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-videochat-webrtc"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/ios-video-calling"> <Icon icon="book" /> Documentation </a> </Card> <Card title="Swift Video Calling Sample App" icon="swift"> <a href="https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-videochat-webrtc-swift"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/ios-video-calling"> <Icon icon="book" /> Documentation </a> </Card> </CardGroup> ### More samples For more samples, head to our [Code Samples](/code-samples/code-samples) page. These sample apps are available on GitHub so feel free to browse them there. Just clone the repository and modify the source code for your own projects. ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app clicking **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click **Add** button. 4. Go to **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** . ## Requirements The minimum requirements for QuickBlox iOS SDK are: * iOS 13.0 * CocoaPods 1.1 * Xcode 11 ## Install QuickBlox SDK into your app You can install the QuickBlox iOS SDK using either SPM (Swift Package Manager) or CocoaPods. ### Swift Package Manager <Warning> QuickBlox iOS SDK is available using the \[Swift Package Manager] ([https://www.swift.org/package-manager/](https://www.swift.org/package-manager/)) (SPM) since version 2.18.1 for QuickBlox and since version 2.8.1 for QuickbloxWebRTC. </Warning> To add QuickBlox iOS SDK to your project using SPM, you can follow these steps: 1. Open your Xcode project and navigate to File > Swift Packages > Add Package Dependency. 2. In the search bar, enter the QuickBlox repository URL: [https://github.com/QuickBlox/ios-quickblox-sdk.git](https://github.com/QuickBlox/ios-quickblox-sdk.git) or QuickbloxWebRTC repository URL: [https://github.com/QuickBlox/ios-quickblox-sdk-webrtc.git](https://github.com/QuickBlox/ios-quickblox-sdk-webrtc.git) and click Add Package. 3. Xcode will then fetch the SDK and you can add it to your project by clicking Add Package. 4. You can then import QuickBlox modules into your code and use its API. <Tabs> <Tab title="Swift"> ```Swift theme={null} import Quickblox import QuickbloxWebRTC ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} #import <Quickblox/Quickblox.h> #import <QuickbloxWebRTC/QuickbloxWebRTC.h> ``` </Tab> </Tabs> For more information on spm customization options, you can refer to the [Apple Documentation](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app). ### CocoaPods <Note> [CocoaPods](https://cocoapods.org) must be installed. </Note> 1. Create a `Podfile`. Project dependencies should be managed by CocoaPods. Create this file in the same directory with your project. ```Bash Bash theme={null} pod init touch Podfile open -e Podfile ``` 2. Open the created `Podfile` and enter the following code lines into it. Specify the SDK version being installed. ```Podfile Podfile theme={null} platform :ios, "12.0" use_frameworks! target 'MyApp' do pod 'QuickBlox', '~> 2.17.10' pod 'Quickblox-WebRTC', '~> 2.7.6' end ``` 3. Install QuickBlox dependencies in your project. ```Bash Bash theme={null} pod install ``` 4. Import headers to start using QuickBlox frameworks. <Tabs> <Tab title="Swift"> ```Swift theme={null} import Quickblox import QuickbloxWebRTC ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} #import <Quickblox/Quickblox.h> #import <QuickbloxWebRTC/QuickbloxWebRTC.h> ``` </Tab> </Tabs> ## Send your first message ### Initialize QuickBlox SDK Initialize the framework with `applicationID`, `authKey`, `authSecret`, and `accountKey`. Add the code below to the **AppDelegate** file located in the root directory of your project: <Tabs> <Tab title="Swift"> ```Swift theme={null} Quickblox.initWithApplicationId(92, authKey: "wJHdOcQSxXQGWx5", authSecret: "BTFsj7Rtt27DAmT", accountKey: "7yvNe17TnjNUqDoPwfqp") ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [Quickblox initWithApplicationId:92 authKey:@"wJHdOcQSxXQGWx5" authSecret:@"BTFsj7Rtt27DAmT" accountKey:@"7yvNe17TnjNUqDoPwfqp"]; ``` </Tab> </Tabs> <Warning> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend and initialize QuickBlox SDK with applicationId and acountKey only. More details you can find in [Initialize QuickBlox SDK without Authorization Key and Secret](/sdks/ios-setup#initialize-quickblox-sdk-without-authorization-key-and-secret) section. </Warning> ### Authorize user Now, it is time to log in with the user. To create a user session, call the `logIn()` method and pass the user login and user password as arguments to it. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let user = try await QBRequest.login("userLogin", password: "userPassword") ``` </Tab> <Tab title="Swift"> ```Swift theme={null} QBRequest.logIn(withUserLogin: "userLogin", password: "userPassword", successBlock: { (response, user) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} [QBRequest logInWithUserLogin:@"userLogin" password:@"userPassword" successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull tUser) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ### Connect to chat Having authorized a user, you can proceed with connecting to the chat server to start using Chat module functionality. Call the `connect()` method and pass the `user.id` and `user.password` to it. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.connect(withUserID: user.id, password: user.password, completion: { (error) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance connectWithUserID:currentUser.ID password:currentUser.password completion:^(NSError * _Nullable error) { }]; ``` </Tab> </Tabs> ### Create dialog QuickBlox provides three types of dialogs: **1-1 dialog**, **group dialog**, and **public dialog**. Learn more about dialogs [here](/sdks/ios-chat-dialogs#create-dialog). Let’s create a simple **1-1 dialog**. Create a `QBChatDialog` instance and set the `dialogID`, `type`, and `occupantIDs` fields. Call the `createDialog()` method and pass the `dialog` to it as an argument. <Tabs> <Tab title="Swift Concurrency"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.private) dialogInfo.occupantIDs = [34] let dialog = try await QBRequest.createDialog(dialogInfo) ``` </Tab> <Tab title="Swift"> ```Swift theme={null} let dialogInfo = QBChatDialog.create(.private) dialogInfo.occupantIDs = [34] QBRequest.createDialog(dialogInfo, successBlock: { (response, dialog) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} QBChatDialog *dialogInfo = [QBChatDialog create:QBChatDialogTypePrivate]; dialogInfo.occupantIDs = @[@34]; // an ID of opponent [QBRequest createDialog:dialogInfo successBlock:^(QBResponse * _Nonnull response, QBChatDialog * _Nonnull dialog) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ### Receive messages To track events in your chat (for example, receive messages, track to whom your messages are delivered and who read them), you must implement the chat delegate methods for your chat controller. Use the `addDelegate()` method to add a listener enabling the app to listen to the received messages. In other words, you subscribe to this event using chat delegate in your chat controller. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBChat.instance.addDelegate(self) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBChat.instance addDelegate: self]; ``` </Tab> </Tabs> Implement the`QBChatDelegate` methods you need in your chat controller. <Tabs> <Tab title="Swift"> ```Swift theme={null} /// MARK: - QBChatDelegate extension YourViewController: QBChatDelegate { // MARK: - Manage chat receive message callback's func chatRoomDidReceive(_ message: QBChatMessage, fromDialogID dialogID: String) { // Called whenever group chat dialog did receive a message. // !!!note Will be called on both recipients' and senders' device (with corrected time from server) } func chatDidReceive(_ message: QBChatMessage) { // Called whenever new private message was received from QBChat. // !!!note Will be called only on recipient device } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} /// MARK: - QBChatDelegate // MARK: - Manage chat receive message callback's - (void)chatDidReceiveMessage:(QBChatMessage *)message { // Called whenever new private message was received from QBChat. // !!!note Will be called only on recipient device } - (void)chatRoomDidReceiveMessage:(QBChatMessage *)message fromDialogID (NSString*)dialogID { // Called whenever group chat dialog did receive a message. // !!!note Will be called on both recepients' and senders' device (with corrected time from server) } ``` </Tab> </Tabs> ### Send message To send a message, create `QBChatMessage` instance. Set the text of the message. Call the `send()` method and pass the `message` as an argument to it. <Tabs> <Tab title="Swift"> ```Swift theme={null} let message = QBChatMessage() message.text = "How are you today?" message.customParameters["save_to_history"] = true let privateDialog = ... privateDialog.send(message) { (error) in } //MARK: ChatDelegate func chatDidReceive(_ message: QBChatMessage) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBChatMessage *message = [[QBChatMessage alloc] init]; message.text = @"How are you today?"; message.customParameters[@"save_to_history"] = @"1"; QBChatDialog *privateDialog = ...; [privateDialog sendMessage:message completionBlock:^(NSError * _Nullable error) { }]; //MARK: ChatDelegate - (void)chatDidReceiveMessage:(QBChatMessage *)message { } (edited) ``` </Tab> </Tabs> <Note> Set the `save_to_history` parameter if you want this message to be saved in chat history. </Note> # AI Features Source: https://docs.quickblox.com/sdks/ios-sdk-ai-features AI Features base on AI Agents ## Overview Starting from version 2.20.0 of QuickBlox iOS SDK, the AI functionality is enabled and based on AI Agents. The QuickBlox iOS SDK provides a range of features to enhance the chat experience. With essential messaging functionalities such as answer assistant, users can engage in more interactive conversations. **Supported features** | Name | Description | | :--------------- | :------------------------------------------------------------------------- | | AI Answer Assist | Generates a draft response based on chat history for the selected message. | | AI Translate | Provides translation based on chat history to selected incoming message. | ## Requirements The minimum requirements for using AI features are: * QuickBlox iOS SDK v2.20.0 * QuickBlox account with activated AI Agents Visit our [Key Concepts](doc:key-concepts) page to get an overall understanding of the most important QuickBlox concepts. Visit our [AI Agent overview](/ai-agent/overview) page to get an overall understanding of the most important AI Agents concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out our [Setup](https://docs.quickblox.com/docs/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See our [Authentication](https://docs.quickblox.com/docs/ios-authentication) page to learn how to do it. 4. Create or update your AI Agent. See our [AI Agent](/ai-agent/manage-agents#create-ai-agent) documentation to learn how to do it. ### Enable AI Extensions 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Extensions** page 2. Select the checkboxes for the features you want to enable. 3. Click the Save button to save changes. <img alt="Robot Icon" /> ### Review your AI Agent To get **ID** of AI Agent to use it as `smartChatAssistantId ` param follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Agent** page 2. Choose the ID of the AI Agent you want to update and click on it. 3. Edit the AI Agent settings. 4. Click the Save button to save changes. <img alt="Robot Icon" /> ## AI Answer Assist QuickBlox AI Answer Assist generates a contextually relevant draft response based on the chat history. Use it to suggest replies that match the conversation context. ### How to use Assist Answer <Tabs> <Tab title="Swift Concurrency"> ```swift theme={null} let smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" let messageToAssist = "What is Quickblox?" let history = [QBAIAnswerAssistHistoryMessage(role: .assistant, message: "History message"), QBAIAnswerAssistHistoryMessage(role: .user, message: "Hi"), QBAIAnswerAssistHistoryMessage(role: .assistant, message: "Hello! How can I assist you today?") ] let result = try await QB.ai.answerAssist(withSmartChatAssistantId: smartChatAssistantId, messageToAssist: messageToAssist, history: history) let answer = result.answer // handle answer ``` </Tab> <Tab title="Swift"> ```swift theme={null} let smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" let messageToAssist = "What is Quickblox?" let history = [QBAIAnswerAssistHistoryMessage(role: .assistant, message: "History message"), QBAIAnswerAssistHistoryMessage(role: .user, message: "Hi"), QBAIAnswerAssistHistoryMessage(role: .assistant, message: "Hello! How can I assist you today?") ] QB.ai.answerAssist(withSmartChatAssistantId: smartChatAssistantId, messageToAssist: messageToAssist, history: history) { result, error in if let error = error { // handle error return } let answer = result.answer // handle answer } ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} NSString *smartChatAssistantId = @"XXXXXXXXXXXXXXXXXXXXXXXX"; NSString *messageToAssist = @"What is Quickblox?"; NSArray *history = @[ [[QBAIAnswerAssistHistoryMessage alloc] initWithRole:QBAIRoleTypeAssistant message:@"History message"], [[QBAIAnswerAssistHistoryMessage alloc] initWithRole:QBAIRoleTypeUser message:@"Hi"], [[QBAIAnswerAssistHistoryMessage alloc] initWithRole:QBAIRoleTypeAssistant message:@"Hello! How can I assist you today?"] ]; [QB.ai answerAssistWithSmartChatAssistantId:smartChatAssistantId messageToAssist:messageToAssist history:history completion:^(id<QBAIAnswerAssistResultProtocol> _Nonnull result, NSError * _Nullable error) { if (error) { // handle error return; } NSString *answer = result.answer; // handle answer }]; ``` </Tab> </Tabs> | Parameter name | Type | Description | | :------------------- | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual Smart Chat Assistant ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | | messageToAssist | String | Message you want to get answer for. | | history | Array of Object | Conversation history. Used to add context. Each object of array should have the two fields: 'role' and 'message'. The field role should contains one of next values: 'user' or 'assistant'. The field message should be a string with chat message. | ## AI Translate QuickBlox offers translation functionality that helps users easily translate text messages in chat, taking into account the context of the chat history. ### How to use AI Translate <Tabs> <Tab title="Swift Concurrency"> ```swift theme={null} let smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" let textToTranslate = "Hola!" let languageCode = "en" let result = try await QB.ai.translate(withSmartChatAssistantId: smartChatAssistantId, textToTranslate: textToTranslate, languageCode: languageCode) let answer = result.answer // handle answer ``` </Tab> <Tab title="Swift"> ```swift theme={null} let smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" let textToTranslate = "Hola!" let languageCode = "en" QB.ai.translate(withSmartChatAssistantId: smartChatAssistantId, textToTranslate: textToTranslate, languageCode: languageCode) { result, error in if let error = error { // handle error return } let answer = result.answer // handle answer } ``` </Tab> <Tab title="Objective-C"> ```objc theme={null} NSString *smartChatAssistantId = @"XXXXXXXXXXXXXXXXXXXXXXXX"; NSString *textToTranslate = @"Hola!"; NSString *languageCode = @"en"; [QB.ai translateWithSmartChatAssistantId:smartChatAssistantId textToTranslate:textToTranslate languageCode:languageCode completion:^(id<QBAITranslateResultProtocol> _Nonnull result, NSError * _Nullable error) { if (error) { // handle error return; } NSString *answer = result.answer; // handle answer }]; ``` </Tab> </Tabs> | Parameter name | Type | Description | | :------------------- | :----- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual Smart Chat Assistant ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | | textToTranslate | String | Text to translate. | | languageCode | String | Translation language code. | # Setup Source: https://docs.quickblox.com/sdks/ios-setup Learn how to add and configure QuickBlox SDK for your app. Follow the instructions below to ensure that QuickBlox iOS SDK runs smoothly with your app. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app clicking **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click **Add** button. 4. Go to **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** . ## Requirements The minimum requirements for QuickBlox iOS SDK are: * iOS 12.0 * CocoaPods 1.1 * Xcode 11 ## Install QuickBlox SDK into your app You can install the QuickBlox iOS SDK using either SPM (Swift Package Manager) or CocoaPods. ### Swift Package Manager <Warning> QuickBlox iOS SDK is available using the \[Swift Package Manager] ([https://www.swift.org/package-manager/](https://www.swift.org/package-manager/)) (SPM) since version 2.18.1 for QuickBlox and since version 2.8.1 for QuickbloxWebRTC. </Warning> To add QuickBlox iOS SDK to your project using SPM, you can follow these steps: 1. Open your Xcode project and navigate to File > Swift Packages > Add Package Dependency. 2. In the search bar, enter the QuickBlox repository URL: [https://github.com/QuickBlox/ios-quickblox-sdk.git](https://github.com/QuickBlox/ios-quickblox-sdk.git) or QuickbloxWebRTC repository URL: [https://github.com/QuickBlox/ios-quickblox-sdk-webrtc.git](https://github.com/QuickBlox/ios-quickblox-sdk-webrtc.git) and click Add Package. 3. Xcode will then fetch the SDK and you can add it to your project by clicking Add Package. 4. You can then import QuickBlox modules into your code and use its API. <Tabs> <Tab title="Swift"> ```Swift theme={null} import Quickblox import QuickbloxWebRTC ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} @import Quickblox; @import QuickbloxWebRTC; ``` </Tab> </Tabs> For more information on spm customization options, you can refer to the [Apple Documentation](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app). ### CocoaPods <Note> [CocoaPods](https://cocoapods.org) must be installed. </Note> 1. Create a `Podfile`. Project dependencies should be managed by CocoaPods. Create this file in the same directory with your project. ```Bash Bash theme={null} pod init touch Podfile open -e Podfile ``` 1. Open the created `Podfile` and enter the following code lines into it. ```Podfile Podfile theme={null} platform :ios, "12.0" use_frameworks! target 'MyApp' do pod 'QuickBlox', '~> 2.17.10' pod 'Quickblox-WebRTC', '~> 2.7.6' end ``` 1. Install QuickBlox dependencies in your project. ```Bash Bash theme={null} pod install ``` 1. Import headers to start using QuickBlox frameworks. <Tabs> <Tab title="Swift"> ```Swift theme={null} import Quickblox import QuickbloxWebRTC ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} #import <Quickblox/Quickblox.h> #import <QuickbloxWebRTC/QuickbloxWebRTC.h> ``` </Tab> </Tabs> ## Run script phase for archiving Add a **Run Script Phase** to build phases of your project. Paste the following snippet into the script. ```Bash Bash theme={null} bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/QuickbloxWebRTC.framework/strip-framework.sh" ``` This fixes the [known Apple bug](http://www.openradar.me/radar?id=6409498411401216) that does not allow to publish archives to the App store with dynamic frameworks that contain simulator platforms. Script is designed to work only for archiving. ## Add permissions You can use our SDK in the background mode as well. If you want to receive push notifications when the app goes to background mode, this requires you to add specific app permissions. Under the app build settings, open the **Capabilities** tab and turn on **Remote notifications** checkbox only. <img alt="add permissions for remote notifications for iOS app" /> If you want to use video calling functionality in the background mode, set the **Audio, AirPlay, and Picture in Picture** checkboxes. <img alt="add permissions for Audio, AirPlay, and Picture in Picture for iOS app" /> <Note> Note that you need to request camera and microphone permissions at runtime. </Note> ## Initialize QuickBlox SDK Initialize the framework with your application credentials. Pass **Application ID**, **Authorization Key** and **Authorization Secret** via **AppDelegate** file located in the root directory of your project. <Tabs> <Tab title="Swift"> ```Swift theme={null} Quickblox.initWithApplicationId(92, authKey: "wJHdOcQSxXQGWx5", authSecret: "BTFsj7Rtt27DAmT", accountKey: "7yvNe17TnjNUqDoPwfqp") ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [Quickblox initWithApplicationId:92 authKey:@"wJHdOcQSxXQGWx5" authSecret:@"BTFsj7Rtt27DAmT" accountKey:@"7yvNe17TnjNUqDoPwfqp"]; ``` </Tab> </Tabs> <Warning> If you have version lower than 2.8.0, use the **QBSettings** class to set application credentials. *QBSettings.applicationID = 92* *QBSettings.authKey = "wJHdOcQSxXQGWx5"* *QBSettings.authSecret = "BTFsj7Rtt27DAmT"* *QBSettings.accountKey = "7yvNe17TnjNUqDoPwfqp"* </Warning> <Warning> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend. </Warning> | Parameters | Description | | ------------- | ------------------------------------------------------------------ | | applicationID | Application identifier. | | authKey | Authorization key. | | authSecret | Authorization secret. | | accountKey | Required to get actual Chat and APIendpoints for the right server. | ## Initialize QuickBlox SDK without Authorization Key and Secret You may don't want to store **authKey** and **authSecret** inside an application for security reasons. In such case, you can initialize QuickBlox SDK with **applicationId** and **accountKey** only, and store your **authKey** and **authSecret** on your backend. But, if so, the implementation of [authentication with QuickBlox](https://docs.quickblox.com/reference/authentication) should be also moved to your backend. <Tabs> <Tab title="Swift"> ```Swift theme={null} Quickblox.initWithApplicationId(92, accountKey: "7yvNe17TnjNUqDoPwfqp") ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [Quickblox initWithApplicationId:92 accountKey:@"7yvNe17TnjNUqDoPwfqp"]; ``` </Tab> </Tabs> Then using your backend you can authorize a user in the QuickBlox system, send back the user session token, and set it to the QuickBlox SDK using `startSession(withToken:)` method. You can find out more about this in the [Set existing session](/sdks/ios-authentication#set-existing-session) section. ## Point SDK to enterprise server To point QuickBlox SDK to the QuickBlox enterprise server, you should specify `apiEndpoint` and `chatEndpoint` in the **AppDelegate** file located in the root directory of your project. <Tabs> <Tab title="Swift"> ```Swift theme={null} Quickblox.initWithApplicationId(92, authKey: "wJHdOcQSxXQGWx5", authSecret: "BTFsj7Rtt27DAmT", accountKey: "7yvNe17TnjNUqDoPwfqp") QBSettings.apiEndpoint = "https://yourApi.quickblox.com" QBSettings.chatEndpoint = "yourChat.quickblox.com" ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [Quickblox initWithApplicationId:92 authKey:@"wJHdOcQSxXQGWx5" authSecret:@"BTFsj7Rtt27DAmT" accountKey:@"7yvNe17TnjNUqDoPwfqp"]; [QBSettings setApiEndpoint:@"https://yourApi.quickblox.com"]; [QBSettings setChatEndpoint:@"yourChat.quickblox.com"]; ``` </Tab> </Tabs> | Parameters | Description | | ------------- | ------------------------------------------------------------------ | | applicationID | Application identifier. | | authKey | Authorization key. | | accountKey | Required to get actual Chat and APIendpoints for the right server. | | apiEndpoint | API endpoint. | | chatEndpoint | Chat endpoint. | <Note> [Contact our sales team](https://quickblox.com/enterprise/) to get **API endpoint** and **chat endpoint**. </Note> ## Selection of the hashing algorithm You can select a hash algorithm using `QBSettings.hashAlgorithm`, which supports **SHA1** and **SHA256** options starting from version 2.19.0. `QBHashAlgorithmSHA1` is set as the default option. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.hashAlgorithm = .SHA256 ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBSettings.hashAlgorithm = QBHashAlgorithmSHA256; ``` </Tab> </Tabs> SwiftObjective-C ## Enable logging Logging functionality allows you to keep track of all events and activities while running your app. As a result, you can monitor the operation of the SDK and improve the debug efficiency. There are 3 logging use cases: * **Server API** logging is used to monitor Server API calls. * **Chat** logging is used to monitor chat issues. * **WebRTC** logging is used to gather issues with video. **Server API logging** Enable Server API calls debug console output using the `setLogLevel()` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.setLogLevel(QBLogLevel.debug) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBSettings setLogLevel:QBLogLevelDebug]; ``` </Tab> </Tabs> A log level determines what information is written to the log. There can be set the following log levels: | Parameters | Description | | ------------------ | ------------------------------------ | | QBLogLevel.debug | Write full logs (**default** value). | | QBLogLevelNetwork | Write network logs. | | QBLogLevelInfo | Write information logs. | | QBLogLevelWarnings | Write warning logs. | | QBLogLevelErrors | Write error logs. | | QBLogLevelNothing | Write nothing. Turn off logs. | **Chat logging** Enable a detailed XMPP logging in the console output using the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.enableXMPPLogging() ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBSettings enableXMPPLogging]; ``` </Tab> </Tabs> Disable XMPP logging using the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.disableXMPPLogging() ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBSettings disableXMPPLogging]; ``` </Tab> </Tabs> **WebRTC logging** Set WebRTC logs using the `setLogLevel()` method. SwiftObjective-C <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCConfig.setLogLevel(QBRTCLogLevel.verboseWithWebRTC) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCConfig.setLogLevel(QBRTCLogLevel.verboseWithWebRTC) ``` </Tab> </Tabs> | Parameters | Description | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------- | | QBRTCLogLevelVerboseWithWebRTC | Write full QuickBlox WebRTC and WebRTC native logs. Can be helpful to debug some complicated problems with calls. | | QBRTCLogLevelVerbose | Write full QuickBlox WebRTC logs. Basic logs from our framework (**default** value). | | QBRTCLogLevelInfo | Write information logs. | | QBRTCLogLevelWarnings | Write warning logs. | | QBRTCLogLevelErrors | Write error logs. | | QBRTCLogLevelNothing | Write nothing. Turn off logs. | ## Enable auto-reconnect to Chat QuickBlox Chat runs over XMPP protocol. To receive messages in a real-time mode, the application should be connected to the Chat over XMPP protocol. To enable auto-reconnect to Chat, pass `autoReconnectEnabled()` as `true`. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.autoReconnectEnabled = true ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBSettings.autoReconnectEnabled = YES; ``` </Tab> </Tabs> <Note> **By default**, the auto-reconnect functionality is disabled. Set the auto-reconnect before calling the `connect()` method so it could be applied in a current chat. </Note> ## Message carbons Message carbons functionality allows for multi-device support. Thus, all user messages get copied to all their devices so they could keep up with the current state of the conversation. For example, a User A has phone running conversations and desktop running conversations. User B has desktop running conversations. When User B sends a message to User A, the message shows on both the desktop and phone of User A. **Enable message carbons** <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.carbonsEnabled = true ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBSettings setCarbonsEnabled: YES]; ``` </Tab> </Tabs> **Disable message carbons** <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.carbonsEnabled = false ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBSettings setCarbonsEnabled: NO]; ``` </Tab> </Tabs> \` <Note> Since message carbons functionality works over XMPP connection, make sure to enable it after the `connect()` method is called. </Note> ## Stream management Stream management has two important features **Stanza Acknowledgements** and **Stream Resumption**: * **Stanza Acknowledgements** is the ability to know if a stanza or series of stanzas has been received by one's peer. In other words, a reply is requested on every sent message. If the reply is received, the message is considered as delivered. * **Stream Resumption** is the ability to quickly resume a stream that has been terminated. Once a connection is re-established, Stream Resumption is executed. By matching the sequence numbers assigned to each Stanza Acknowledgement a server and client can verify which messages are missing and request to resend missing messages. Set message timeout to enable stream resumption. The preferred resumption time should be set in seconds. If this parameter is greater than `0`, then it is applied, otherwise, it is not applied. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBSettings.streamManagementSendMessageTimeout = 0 ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBSettings setStreamManagementSendMessageTimeout: 0]; ``` </Tab> </Tabs> <Note> Stream management gets enabled automatically once the `connect()` method is called and disabled once the `disconnect()` method is called. </Note> ## Custom ICE servers You can customize a list of ICE servers. **By default**, WebRTC module will use internal ICE servers that are usually enough, but you can always set your own. WebRTC engine will choose the TURN relay with the lowest round-trip time. Thus, setting multiple TURN servers allows your application to scale-up in terms of bandwidth and number of users. Set up ICE servers in `application(_:didFinishLaunchingWithOptions:)` method of `AppDelegate.m`. <Tabs> <Tab title="Swift"> ```Swift theme={null} let username = "login" let password = "767565gfh865486h548k6586h5868" let urls = [ "stun:turn.randomserver.example", "turn:turn.randomserver.example:5677?transport=udp", "turn:turn.randomserver.example:5677?transport=tcp" ] guard let server = QBRTCICEServer.init(urls: urls, username: username, password: password) else { return } QBRTCConfig.setICEServers([server]) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *userName = @"login"; NSString *password = @"767565gfh865486h548k6586h5868"; NSArray *urls = @[ @"stun:turn.randomserver.example", @"turn:turn.randomserver.example:5677?transport=udp", @"turn:turn.randomserver.example:5677?transport=tcp" ]; QBRTCICEServer *server = [QBRTCICEServer serverWithURLs:urls username:userName password:password]; [QBRTCConfig setICEServers:@[server]]; ``` </Tab> </Tabs> # Users Source: https://docs.quickblox.com/sdks/ios-users Learn how to manage your users with QuickBlox. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. ## Create user It's recommended to manage user creation at your backend for production. To learn more you can refer to [QuickBlox API documentation](https://docs.quickblox.com/reference/create-user). For POCs/MVPs or during development you may want to create users on the fly, you can use `signUp()` method. Create a user using the code snippet below. Only login (or email) and password are required. Other fields are optional. <Tabs> <Tab title="Swift"> ```Swift theme={null} let user = QBUUser() user.login = "myLogin" user.fullName = "myFullName" user.password = "myPassword" QBRequest.signUp(user, successBlock: { (response, user) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBUUser *user = [[QBUUser alloc] init]; user.login = @"myLogin"; user.fullName = @"myFullName"; user.password = @"myPassword"; [QBRequest signUp:user successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull user) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> <Warning> **Security & Privacy** It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. Email, full name, facebookId and phone number are PII, [configure session permissions](/docs/application#set-session-permissions) according to your privacy requirements. </Warning> ## Retrieve users Get a list of users using the `users(withExtendedRequest:page:)` method. The code snippet below shows how to get a list of users created between the two given dates and sorted in descending order. <Tabs> <Tab title="Swift"> ```Swift theme={null} let field = "created_at" let typeField = "date" let sortDesc = "desc" let sortAsc = "asc" let paramSort = "order" let paramFilter = "filter[]" let searchOperator = "between" let searchValue = "2021-01-01, 2021-05-06" var extendedRequest: [String: String] = [:] extendedRequest[paramSort] = sortDesc + " " + typeField + " " + field extendedRequest[paramFilter] = typeField + " " + field + " " + searchOperator + " " + searchValue let page = QBGeneralResponsePage(currentPage: 1, perPage: 100) QBRequest.users(withExtendedRequest: extendedRequest, page: page, successBlock: { (response, page, users) in }, errorBlock: { response in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *field = @"created_at"; NSString *typeField = @"date"; NSString *sortDesc = @"desc"; NSString *sortAsc = @"asc"; NSString *paramSort = @"order"; NSString *paramFilter = @"filter[]"; NSString *searchOperator = @"between"; NSString *searchValue = @"2021-01-01, 2021-05-06"; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; extendedRequest[paramSort] = [NSString stringWithFormat:@"%@ %@ %@", sortDesc, typeField, field]; extendedRequest[paramFilter] = [NSString stringWithFormat:@"%@ %@ %@ %@", typeField, field, searchOperator, searchValue]; QBGeneralResponsePage *page = [QBGeneralResponsePage responsePageWithCurrentPage:1 perPage:10]; [QBRequest usersWithExtendedRequest:extendedRequest.copy page:page successBlock:^(QBResponse * _Nonnull response, QBGeneralResponsePage * _Nonnull page, NSArray<QBUUser *> * _Nonnull users) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | extendedRequest | yes | A dictionary that stores keys and values of the String type. | | page | no | If you want to get a paginated list of users from the server, you can set the following fields of the page:- currentpage is a number of pages with results to be returned.- perPage is a number of records to return in one page. | If you want to retrieve only users updated after some specific date time, you can use operators. This is useful if you cache users somehow and do not want to obtain the whole list of users on every app start. Thus, you can use [search](/sdks/ios-users#search-operators) and [sort](/sdks/ios-users#sort-operators) operators to list users on the page so that it is easier to see specific users. ### Search operators You can use search operators to get more specific search results. The request below will return a list of users created between the two given dates. <Tabs> <Tab title="Swift"> ```Swift theme={null} let field = "created_at" let typeField = "date" let paramFilter = "filter[]" let searchOperator = "between" let searchValue = "2021-01-01, 2021-05-06" var extendedRequest: [String: String] = [:] extendedRequest[paramFilter] = typeField + " " + field + " " + searchOperator + " " + searchValue let page = QBGeneralResponsePage(currentPage: 1, perPage: 100) QBRequest.users(withExtendedRequest: extendedRequest, page: page, successBlock: { (response, page, users) in }, errorBlock: { response in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *field = @"created_at"; NSString *typeField = @"date"; NSString *paramFilter = @"filter[]"; NSString *searchOperator = @"between"; NSString *searchValue = @"2021-01-01, 2021-05-06"; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; extendedRequest[paramFilter] = [NSString stringWithFormat:@"%@ %@ %@ %@", typeField, field, searchOperator, searchValue]; QBGeneralResponsePage *page = [QBGeneralResponsePage responsePageWithCurrentPage:1 perPage:10]; [QBRequest usersWithExtendedRequest:extendedRequest.copy page:page successBlock:^(QBResponse * _Nonnull response, QBGeneralResponsePage * _Nonnull page, NSArray<QBUUser *> * _Nonnull users) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ | | lt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less Than** operator. | | gt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** operator. | | gte | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** or **Equal** to operator. | | le | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less or Equal to** operator | | eq | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Equal** to operator. | | ne | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Not Equal** to operator. | | between | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Contained between values** operator. | | in | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **IN** array operator. | ### Sort operators You can use sort operators to order the search results. The request below will return a list of users sorted in descending order by the `created_at` field. <Tabs> <Tab title="Swift"> ```Swift theme={null} let field = "created_at" let typeField = "date" let sortDesc = "desc" let sortAsc = "asc" let paramSort = "order" var extendedRequest: [String: String] = [:] extendedRequest[paramSort] = sortDesc + " " + typeField + " " + field let page = QBGeneralResponsePage(currentPage: 1, perPage: 100) QBRequest.users(withExtendedRequest: extendedRequest, page: page, successBlock: { (response, page, users) in }, errorBlock: { response in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *field = @"created_at"; NSString *typeField = @"date"; NSString *sortDesc = @"desc"; NSString *sortAsc = @"asc"; NSString *paramSort = @"order"; NSMutableDictionary *extendedRequest = [NSMutableDictionary dictionary]; extendedRequest[paramSort] = [NSString stringWithFormat:@"%@ %@ %@", sortDesc, typeField, field]; QBGeneralResponsePage *page = [QBGeneralResponsePage responsePageWithCurrentPage:1 perPage:10]; [QBRequest usersWithExtendedRequest:extendedRequest.copy page:page successBlock:^(QBResponse * _Nonnull response, QBGeneralResponsePage * _Nonnull page, NSArray<QBUUser *> * _Nonnull users) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> Here are the sort operators you can use to order the search results. | Sort operator | Applicable to types | Applicable to fields | Description | | ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | | asc | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Search results will be sorted in ascending order by the specified field. | | desc | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Search results will be sorted in descending order by the specified field. | ## Retrieve users by ID To get a list of users by ID for a current account, use the following code snippet. <Tabs> <Tab title="Swift"> ```Swift theme={null} let page = QBGeneralResponsePage(currentPage: 5, perPage: 10) QBRequest.users(withIDs: ["21","22"], page: page, successBlock: { (response, page, users) in }, errorBlock:{ (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBGeneralResponsePage *page = [QBGeneralResponsePage responsePageWithCurrentPage:5 perPage:10]; [QBRequest usersWithIDs:@[@"20", @"21"] page:page successBlock:^(QBResponse * _Nonnull response, QBGeneralResponsePage * _Nonnull page, NSArray<QBUUser *> * _Nonnull users) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Retrieve user by login To get a list of users by login for a current account, use the following code snippet. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.user(withLogin: "amigo", successBlock: { (response, user) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest userWithLogin:@"amigo" successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull user) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Retrieve user by email To get a list of users by email for a current account, use the following code snippet. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.user(withEmail: "[[email protected]](/cdn-cgi/l/email-protection)", successBlock: { (response, user) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest userWithEmail:@"[[email protected]](/cdn-cgi/l/email-protection)" successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull user) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Retrieve users by full name To get a list of users found by the server according to the search query, use the following code snippet. Search **requires** min 3 characters. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.users(withFullName: "Amigo", page: nil, successBlock: { (response, page, users) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest usersWithFullName:@"Amigo" page:nil successBlock:^(QBResponse * _Nonnull response, QBGeneralResponsePage * _Nonnull page, NSArray<QBUUser *> * _Nonnull users) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Retrieve users by phone number To get a list of users by phone number for a current account, use the following code snippet. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.users(withPhoneNumbers: ["+4427123314"], page: nil, successBlock: { (response, page, users) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest usersWithPhoneNumbers:@[@"+4427123314"] page:nil successBlock:^(QBResponse * _Nonnull response, QBGeneralResponsePage * _Nonnull page, NSArray<QBUUser *> * _Nonnull users) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Retrieve user by external user ID If you have your own database with users (we call these databases as "external databases"), you can use External User ID (`ExternalID` field) in `QBUser` model to link users from QuickBlox with users from your external database. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.user(withExternalID: 3789, successBlock: { response, user in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest userWithExternalID:3789 successBlock:^(QBResponse * _Nonnull response, QBUUser *user) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Retrieve users by tags To get a list of users by tags for a current account, use the following code snippet. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.users(withTags: ["iphone"], page: nil, successBlock: { (response, page, users) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest usersWithTags:@[@"iphone"] page:nil successBlock:^(QBResponse * _Nonnull response, QBGeneralResponsePage * _Nonnull page, NSArray<QBUUser *> * _Nonnull users) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Delete user A user can delete himself from the platform. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.deleteCurrentUser(successBlock: { (response) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest deleteCurrentUserWithSuccessBlock:^(QBResponse * _Nonnull response) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> ## Reset user password It's possible to reset a password via email. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRequest.resetUserPassword(withEmail: "[[email protected]](/cdn-cgi/l/email-protection)", successBlock: { (response) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRequest resetUserPasswordWithEmail:@"[[email protected]](/cdn-cgi/l/email-protection)" successBlock:^(QBResponse * _Nonnull response) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> <Note> Make sure to enable the email confirmation. This functionality allows application users to confirm their emails. If a user doesn't confirm the email, the emails won't be sent to this user. As a result, a password reset functionality won't work. To enable the email confirmation, proceed as follows: 1. Go to the **Dashboard => *YOUR\_APP* => Users => Settings => User registration confirmation** and check the box. 2. Click the **Save** button. </Note> <Warning> A password reset functionality is available for the Enterprise plan. [Contact the sales team](https://quickblox.com/enterprise/#get) for more details. </Warning> ## Update user Update a user profile by calling the `updateUser()` method. If you want to change your password, you need to provide 2 parameters: `password` and `oldPassword`. The updated user entity will be returned. <Warning> You must set `null` into the user's `password` field if you do not want to change your password, otherwise, you will need to add the user's old password to the `oldPassword` field. </Warning> You can update any other field of the user using the `updateUser()` method. Thus, the snippet below shows how to update a `tagList` and `customData` fields. <Tabs> <Tab title="Swift"> ```Swift theme={null} guard let currentUser = QBSession.current.currentUser else { return } var customData:[String: String] = [:] if let data = currentUser.customData?.data(using: .utf8), let currentUserCustomData = try? JSONSerialization.jsonObject(with: data, options: []) as! [String : String] { customData = currentUserCustomData } customData["name"] = "John" customData["age"] = "31" customData["city"] = "New York" let updateUserParameter = QBUpdateUserParameters() guard let customJSONData = try? JSONSerialization.data(withJSONObject: customData, options: .prettyPrinted) else { return } updateUserParameter.customData = String(data: customJSONData, encoding: .utf8) //example of custom data JSON string let userCustomDataJsonStringExample = "{\"name\" : \"John\", \"age\" : \"31\", \"city\" : \"New York\"}" updateUserParameter.tags = ["quickblox", "test", "QADev", "chat", "videochat", "webrtc", "conference"] QBRequest.updateCurrentUser(updateUserParameter, successBlock: {response, user in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBUUser *currentUser = QBSession.currentSession.currentUser; NSMutableDictionary *customData = [NSMutableDictionary dictionary]; if (currentUser.customData) { NSData *currentUserJsonData = [currentUser.customData dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *currentUserCustomData = [NSJSONSerialization JSONObjectWithData:currentUserJsonData options:kNilOptions error:nil]; customData = currentUserCustomData.mutableCopy; } customData[@"name"] = @"John"; customData[@"age"] = @"31"; customData[@"city"] = @"New York"; QBUpdateUserParameters *updateUserParameter = [[QBUpdateUserParameters alloc] init]; NSData *jsonCustomData = [NSJSONSerialization dataWithJSONObject:customData options:NSJSONWritingPrettyPrinted error:nil]; NSString *jsonCustomDataString = [[NSString alloc] initWithData:jsonCustomData encoding:NSUTF8StringEncoding]; updateUserParameter.customData = jsonCustomDataString; //example of custom data JSON string NSString *userCustomDataJsonStringExample = @"{\"name\" : \"John\", \"age\" : \"31\", \"city\" : \"New York\"}"; updateUserParameter.tags = @[@"quickblox", @"test", @"QADev", @"chat", @"videochat", @"webrtc", @"conference"]; [QBRequest updateCurrentUser:updateUserParameter successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull user) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Field | Required | Description | | ---------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | tags | no | User tags. An array of Strings. A tag must include alphabetic characters only. The tag must be at least 3 and no more than 15 characters long. There are no spaces in the tag format. For example, the "tagOne" format is correct while the "tag one" format is incorrect. The maximum number of tags is 10. If more than 10 tags are provided, an error is returned: tag list should contain maximum 10 tags. | | customData | no | User custom data. Should be a String. You can convert any data types to String, or example, JSON, XML, etc. | ## Set user avatar To set a user avatar, just upload a file to the QuickBlox cloud storage and connect it to the user. To upload the file to the QuickBlox cloud storage, just convert the file to the date format and upload it to the QuickBlox cloud storage by calling the `tUploadFile(fileName:contentType:isPublic:)` method. In the response, the `uploadedBlob` object is returned by the server. Get the blob UID from the `uploadedBlob` object. To connect the file to the user, set the blob UID in the `customData` field of the `user` model and call the `updateCurrentUser()` method. As a result, the user avatar gets updated. <Tabs> <Tab title="Swift"> ```Swift theme={null} let image = UIImage(systemName: "person.circle") guard let imageData = image?.pngData() else { return } let fileName = "avatar.png" let contentType = "image/png" let isPublic = true QBRequest.tUploadFile(imageData, fileName: fileName, contentType: contentType, isPublic: isPublic, successBlock: { (response, uploadedBlob) in let parameters = QBUpdateUserParameters() parameters.blobID = uploadedBlob.id QBRequest.updateCurrentUser(parameters, successBlock: { (response, user) in }, errorBlock: { (response) in }) }, statusBlock: { (request, status) in }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} UIImage *image = [UIImage systemImageNamed:@"person.circle"]; NSData *imageData = UIImagePNGRepresentation(image); NSString *fileName = @"avatar.png"; NSString *contentType = @"image/png"; BOOL isPublic = TRUE; [QBRequest TUploadFile:imageData fileName:fileName contentType:contentType isPublic:isPublic successBlock:^(QBResponse * _Nonnull response, QBCBlob * _Nonnull uploadedBlob) { QBUpdateUserParameters *parameters = [[QBUpdateUserParameters alloc] init]; parameters.blobID = uploadedBlob.ID; [QBRequest updateCurrentUser:parameters successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull user) { } errorBlock:^(QBResponse * _Nonnull response) { }]; } statusBlock:^(QBRequest * _Nonnull request, QBRequestStatus * _Nonnull status) { } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> Pass the following arguments to the `updateCurrentUser()` method. | Argument | Required | Description | | ---------- | -------- | ----------------------------------------------- | | parameters | yes | Specifies parameters fields that should be set. | ## Get user avatar Now, other users can get your avatar by calling the `downloadFile(withUID:)` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} let userAvatarImageView = UIImageView() let user = QBSession.current.currentUser QBRequest.blob(withID: user.blobID, successBlock: { (response, blob) in guard let blobUID = blob.uid else {return} QBRequest.downloadFile(withUID: blobUID, successBlock: { (response, fileData) in if let image = UIImage(data: fileData) { userAvatarImageView.image = image } }, statusBlock: { (request, status) in }, errorBlock: { (response) in }) }, errorBlock: { (response) in }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} UIImageView *userAvatarImageView = [[UIImageView alloc] init]; QBUUser *user = QBSession.currentSession.currentUser; [QBRequest blobWithID:user.blobID successBlock:^(QBResponse * _Nonnull response, QBCBlob * _Nonnull tBlob) { [QBRequest downloadFileWithUID:tBlob.UID successBlock:^(QBResponse * _Nonnull response, NSData * _Nonnull fileData) { UIImage *image = [UIImage imageWithData:fileData]; userAvatarImageView.image = image; } statusBlock:^(QBRequest * _Nonnull request, QBRequestStatus * _Nonnull status) { } errorBlock:^(QBResponse * _Nonnull response) { }]; } errorBlock:^(QBResponse * _Nonnull response) { }]; ``` </Tab> </Tabs> | Argument | Required | Description | | --------- | -------- | ----------------------- | | avatarUID | yes | Blob unique identifier. | # Basic Source: https://docs.quickblox.com/sdks/ios-video-calling Learn how to add peer-to-peer video calls to your app. QuickBlox Video Calling API is built on top of [WebRTC](https://webrtc.org/). It allows adding real-time video communication features into your app similar to Skype using API easily. The communication is happening between peers representing camera devices. There are two **peer types**: \- **Local peer** is a device running the app right now. \- **Remote peer** is an opponent device. Establishing real-time video communication between two peers involves 3 phases: \- **Signaling**. At this phase, the peers’ local IPs and ports where they can be reached (ICE candidates) are exchanged as well as their media capabilities and session control messages. \- **Discovery**. At this phase, the public IPs and ports at which endpoints can be reached are discovered by STUN/TURN server. \- **Establishing a connection**. At this phase, the data are sent directly to each party of the communication process. <Warning> In order to start using Video Calling Module, you need to **connect to QuickBlox Chat first**. The signaling in the QuickBox WebRTC module is implemented over the XMPP protocol using QuickBlox Chat Module. It acts as a signaling transport for Video Calling API. </Warning> <Note> Please use this WebRTC Video Calling to make the Group Calls with **4 or fewer users**. Because of [Mesh architecture](https://webrtcglossary.com/mesh/) we use for multi-point where every participant sends and receives its media to all other participants, the current solution supports group calls with up to 4 people. </Note> Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/ios-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/ios-authentication) page to learn how to do it. 4. Connect to the Chat server to provide a signaling mechanism for Video Calling API. Follow our [Chat](/sdks/ios-chat) page to learn about chat connection settings and configuration. ## Initialize WebRTC Before any interaction with QuickbloxWebRTC, you need to initialize it using the method below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCClient.initializeRTC(); ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCClient initializeRTC]; ``` </Tab> </Tabs> ### Logging Logging is a powerful tool to see the exact flow of the QuickbloxWebRTC framework and analyze its decisions. By enabling logs you will be able to debug most issues, or perhaps help us analyze your problems. Check [Enable logging](/sdks/ios-setup#enable-logging) section to learn how to enable logging. ### Background mode You can use our SDK in the background mode as well, however, this requires you to add a specific app **permissions**. Under the **app build settings**, open the **Capabilities tab**. In this tab, turn on **Background Modes** and set the **Audio, AirPlay and Picture in Picture** checkbox to set the audio background mode. <img alt="add permission for Audio, AirPlay and Picture in Picture of iOS app" /> If everything is correctly configured, iOS provides an indicator that your app is running in the background with an active audio session. This is seen as a red background of the status bar, as well as an additional bar indicating the name of the app holding the active audio session, in this case - your app. ## Manage calls In order to operate and receive calls you need to setup a client delegate. Your class must conform to the `QBRTCClientDelegate` protocol. Use the method below to subscribe. Learn more details about the event delegate configuration in the [Event delegate](#event-delegate) section. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCClient.instance().add(self) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCClient.instance addDelegate:self]; ``` </Tab> </Tabs> ## Initiate a call To call other users, use `QBRTCClient` and `QBRTCSession` methods below. <Tabs> <Tab title="Swift"> ```Swift theme={null} // 2123, 2123, 3122 - opponent's let opponentsIDs = [3245, 2123, 3122] let newSession = QBRTCClient.instance().createNewSession(withOpponents: ids! as [NSNumber], with: .video) // userInfo - the custom user information dictionary for the call. May be nil. let userInfo = ["key":"value"] // optional newSession.startCall(userInfo) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // 2123, 2123, 3122 - opponent's NSArray *opponentsIDs = @[@3245, @2123, @3122]; QBRTCSession *newSession = [QBRTCClient.instance createNewSessionWithOpponents:opponentsIDs withConferenceType:QBRTCConferenceTypeVideo]; // userInfo - the custom user information dictionary for the call. May be nil. NSDictionary *userInfo = @{ @"key" : @"value" }; // optional [newSession startCall:userInfo]; ``` </Tab> </Tabs> After this, your opponents will receive **one** call request per **five** seconds for a duration of **45** seconds (you can configure these settings with `QBRTCConfig`). <Tabs> <Tab title="Swift"> ```Swift theme={null} func didReceiveNewSession(_ session: QBRTCSession, userInfo: [String : String]? = nil) { if self.session != nil { // we already have a video/audio call session, so we reject another one // userInfo - the custom user information dictionary for the call from caller. May be nil. let userInfo = ["key":"value"] // optional session.rejectCall(userInfo) return } // saving session instance here self.session = session } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)didReceiveNewSession:(QBRTCSession *)session userInfo:(NSDictionary *)userInfo { if (self.session) { // we already have a video/audio call session, so we reject another one // userInfo - the custom user information dictionary for the call from caller. May be nil. NSDictionary *userInfo = @{ @"key" : @"value" }; // optional [session rejectCall:userInfo]; return; } // saving session instance here self.session = session; } ``` </Tab> </Tabs> The `self.session` refers to the current call session. Each particular audio/video call has a unique `sessionID`. This allows you to have **more than one** independent audio/video conference calls. If you want to increase the call timeout, you can increase it up to **60** seconds **at maximum**. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCConfig.setAnswerTimeInterval(60) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCConfig setAnswerTimeInterval:60]; ``` </Tab> </Tabs> <Note> **Note** **By default**, `setAnswerTimeInterval` value is 45 seconds. </Note> In case the opponent did not respond to your call within a specific timeout time, the method listed below will be called. <Tabs> <Tab title="Swift"> ```Swift theme={null} func session(_ session: QBRTCSession, userDidNotRespond userID: NSNumber) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)session:(QBRTCSession *)session userDidNotRespond:(NSNumber *)userID { } ``` </Tab> </Tabs> ## Accept a call In order to accept a call, use the `acceptCall()` method below. <Tabs> <Tab title="Swift"> ```Swift theme={null} // userInfo - the custom user information dictionary for the accept call. May be nil. let userInfo = ["key":"value"] // optional self.session?.acceptCall(userInfo) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // userInfo - the custom user information dictionary for the accept call. May be nil. NSDictionary *userInfo = @{ @"key" : @"value" }; // optional [self.session acceptCall:userInfo]; ``` </Tab> </Tabs> After this your opponent will receive an **accept** signal: <Tabs> <Tab title="Swift"> ```Swift theme={null} func session(_ session: QBRTCSession, acceptedByUser userID: NSNumber, userInfo: [String : String]? = nil) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)session:(QBRTCSession *)session acceptedByUser:(NSNumber *)userID userInfo:(NSDictionary *)userInfo { } ``` </Tab> </Tabs> ## Reject a call In order to reject a call, use the `rejectCall()` method below. <Tabs> <Tab title="Swift"> ```Swift theme={null} // userInfo - the custom user information dictionary for the reject call. May be nil. let userInfo = ["key":"value"] // optional self.session?.rejectCall(userInfo) // and release session instance self.session = nil ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // userInfo - the custom user information dictionary for the reject call. May be nil. NSDictionary *userInfo = @{ @"key" : @"value" }; // optional [self.session rejectCall:userInfo]; // and release session instance self.session = nil; ``` </Tab> </Tabs> After this, your opponent will receive a **reject** signal. <Tabs> <Tab title="Swift"> ```Swift theme={null} func session(_ session: QBRTCSession, rejectedByUser userID: NSNumber, userInfo: [String : String]? = nil) { print("Rejected by user \(userID)") } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)session:(QBRTCSession *)session rejectedByUser:(NSNumber *)userID userInfo:(NSDictionary *)userInfo { NSLog(@"Rejected by user %@", userID); } ``` </Tab> </Tabs> ## End a call To end a call, use the `hangUp()` method below. <Tabs> <Tab title="Swift"> ```Swift theme={null} // userInfo - the custom user information dictionary for the reject call. May be nil. let userInfo = ["key":"value"] // optional self.session?.hangUp(userInfo) // and release session instance self.session = nil ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // userInfo - the custom user information dictionary for the reject call. May be nil. NSDictionary *userInfo = @{ @"key" : @"value" }; // optional [self.session hangUp:userInfo]; // and release session instance self.session = nil; ``` </Tab> </Tabs> After this, your opponent will receive a **hangup** signal. <Tabs> <Tab title="Swift"> ```Swift theme={null} func session(_ session: QBRTCSession, hungUpByUser userID: NSNumber, userInfo: [String : String]? = nil) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)session:(QBRTCSession *)session hungUpByUser:(NSNumber *)userID userInfo:(NSDictionary<NSString *,NSString *> *)userInfo { } ``` </Tab> </Tabs> ## Local video view To show your **local video track** from a camera, you should create `UIView` on the storyboard and then use the following code. <Tabs> <Tab title="Swift"> ```Swift theme={null} // your view controller interface code import Foundation class CallController: UIViewController, QBRTCClientDelegate { @IBOutlet weak var localVideoView : UIView! // your video view to render local camera video stream var videoCapture: QBRTCCameraCapture? var session: QBRTCSession? override func viewDidLoad() { QBRTCClient.instance().add(self as QBRTCClientDelegate) let videoFormat = QBRTCVideoFormat() videoFormat.frameRate = 30 videoFormat.pixelFormat = .format420f videoFormat.width = 640 videoFormat.height = 480 // QBRTCCameraCapture class used to capture frames using AVFoundation APIs self.videoCapture = QBRTCCameraCapture(videoFormat: videoFormat, position: .front) // add video capture to session's local media stream self.session?.localMediaStream.videoTrack.videoCapture = self.videoCapture self.videoCapture?.previewLayer.frame = self.localVideoView.bounds self.videoCapture?.startSession() self.localVideoView.layer.insertSublayer(self.videoCapture!.previewLayer, atIndex: 0) // start call } //... } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // your view controller interface code @interface CallController()<QBRTCClientDelegate> @property (weak, nonatomic) IBOutlet UIView *localVideoView; // your video view to render local camera video stream @property (strong, nonatomic) QBRTCCameraCapture *videoCapture; @property (strong, nonatomic) QBRTCSession *session; @end @implementation CallController - (void)viewDidLoad { [super viewDidLoad]; [[QBRTCClient instance] addDelegate:self]; QBRTCVideoFormat *videoFormat = [[QBRTCVideoFormat alloc] init]; videoFormat.frameRate = 30; videoFormat.pixelFormat = QBRTCPixelFormat420f; videoFormat.width = 640; videoFormat.height = 480; // QBRTCCameraCapture class used to capture frames using AVFoundation APIs self.videoCapture = [[QBRTCCameraCapture alloc] initWithVideoFormat:videoFormat position:AVCaptureDevicePositionFront]; // or AVCaptureDevicePositionBack // add video capture to session's local media stream self.session.localMediaStream.videoTrack.videoCapture = self.videoCapture; self.videoCapture.previewLayer.frame = self.localVideoView.bounds; [self.videoCapture startSession]; [self.localVideoView.layer insertSublayer:self.videoCapture.previewLayer atIndex:0]; // start call } // ... ``` </Tab> </Tabs> ## Remote video view To show video views with streams that you have received from your opponents, you should create `QBRTCRemoteVideoView` views on the storyboard and then use the following code. <Tabs> <Tab title="Swift"> ```Swift theme={null} func session(_ session: QBRTCBaseSession, receivedRemoteVideoTrack videoTrack: QBRTCVideoTrack, fromUser userID: NSNumber) { // we suppose you have created UIView and set it's class to RemoteVideoView class // also we suggest you to set view mode to UIViewContentModeScaleAspectFit or // UIViewContentModeScaleAspectFill self.opponentVideoView.setVideoTrack(videoTrack) } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)session:(QBRTCBaseSession *)session receivedRemoteVideoTrack:(QBRTCVideoTrack *)videoTrack fromUser:(NSNumber *)userID { // we suppose you have created UIView and set it's class to QBCallRemoteVideoView class // also we suggest you to set view mode to UIViewContentModeScaleAspectFit or // UIViewContentModeScaleAspectFill [self.opponentVideoView setVideoTrack:videoTrack]; } ``` </Tab> </Tabs> You can always get **remote video tracks** for a specific user ID in the call using the below-specified `QBCallSession` methods (assuming that they are existent). <Tabs> <Tab title="Swift"> ```Swift theme={null} let remoteVideoTrack = self.session?.remoteVideoTrack(withUserID: 24450) // video track for user 24450 ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCVideoTrack *remoteVideoTrack = [self.session remoteVideoTrackWithUserID:@(24450)]; // video track for user 24450 ``` </Tab> </Tabs> ## Event delegate To process events such as incoming call, call reject, hang up, etc. you need to set up the event listener. The event listener processes various events that happen with the **call session** or **peer connection** in your app. Using the callbacks provided by the event delegate, you can implement and execute the event-related processing code. For example, the `session(_:acceptedByUser:userInfo:)` method of the `QBRTCClientDelegate` is called when your call has been accepted by the user. This callback receives information about the call session, user ID who accepted the call and additional key-value data about the user. QuickBlox iOS SDK persistently interacts with the server via **XMPP** connection that works as a **signaling** transport for establishing a call between two or more peers. It receives the callbacks of the asynchronous events which happen with the call and peer connection. This allows you to track these events and build your own video calling features around them. To track call session events, you should use `QBRTCSessionEventsCallback` listener. The supported event callbacks for a call session and peer connection are listed in the table below. | Method | Invoked when | | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | didReceiveNewSession(\_:userInfo:) | A new call session has been received. | | session(\_:acceptedByUser:userInfo:) | A call session has been accepted. | | session(\_:rejectedByUser:userInfo:) | A call session has been rejected. | | session(\_:hungUpByUser:userInfo:) | An accepted call has been ended by the peer by pressing the hang-up button. | | session(\_:userDidNotRespond:) | A remote peer did not respond to your call within the timeout period. | | session(\_:sessionDidClose:) | A call session has been closed. | | session(\_:updatedStatsReport:forUserID:) | An updated stats report, which is called by timeout, has been received for the user ID. | | session(\_:didChangeState:) | A call session state has been changed in real-time. View all available call session states in the [Call session states](#call-session-states) section. | | session(\_:receivedRemoteAudioTrack:fromUser:) | A remote **audio** track has been received from the peer. | | session(\_: receivedRemoteVideoTrack:fromUser:) | A remote **video** track has been received from the peer. | | session(\_:startedConnectingToUser:) | A peer connection has been initiated. | | session(\_:connectedToUser:) | A peer connection has been established. | | session(\_:connectionFailedForUser:) | A peer connection has failed. | | session(\_:disconnectedFromUser:) | A connection was terminated. | | session(\_:didChange:forUser:) | A peer connection state has been changed. View all available peer connection states in the [Peer connection states](#peer-connection-states) section. | | session(\_:connectionClosedForUser:) | A peer connection has been closed. | | session(\_:didChangeRconnectionState:forUser:) | A call reconnection state has been changed in real-time. View all available call reconnection states in the [Call reconnection states](#call-reconnection-states) section. | The following code lists all supported event callbacks for the call session and peer connection along with their parameters as well as shows how to add the listener. <Tabs> <Tab title="Swift"> ```Swift theme={null} //CallViewController.swift class CallViewController: UIViewController { //MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() QBRTCClient.instance().add(self as QBRTCClientDelegate) } extension CallViewController: QBRTCClientDelegate { // MARK: QBRTCClientDelegate func didReceiveNewSession(_ session: QBRTCSession, userInfo: [String : String]? = nil) { } func session(_ session: QBRTCSession, userDidNotRespond userID: NSNumber) { } func session(_ session: QBRTCSession, rejectedByUser userID: NSNumber, userInfo: [String : String]? = nil) { } func session(_ session: QBRTCSession, acceptedByUser userID: NSNumber, userInfo: [String : String]? = nil) { } func session(_ session: QBRTCSession, hungUpByUser userID: NSNumber, userInfo: [String : String]? = nil) { } func sessionDidClose(_ session: QBRTCSession) { } // MARK: QBRTCBaseClientDelegate func session(_ session: QBRTCBaseSession, didChange state: QBRTCSessionState) { } func session(_ session: QBRTCBaseSession, updatedStatsReport report: QBRTCStatsReport, forUserID userID: NSNumber) { } func session(_ session: QBRTCBaseSession, receivedRemoteAudioTrack audioTrack: QBRTCAudioTrack, fromUser userID: NSNumber) { } func session(_ session: QBRTCBaseSession, receivedRemoteVideoTrack videoTrack: QBRTCVideoTrack, fromUser userID: NSNumber) { } func session(_ session: QBRTCBaseSession, connectionClosedForUser userID: NSNumber) { } func session(_ session: QBRTCBaseSession, startedConnectingToUser userID: NSNumber) { } func session(_ session: QBRTCBaseSession, connectedToUser userID: NSNumber) { } func session(_ session: QBRTCBaseSession, disconnectedFromUser userID: NSNumber) { } func session(_ session: QBRTCBaseSession, connectionFailedForUser userID: NSNumber) { } func session(_ session: QBRTCBaseSession, didChange state: QBRTCConnectionState, forUser userID: NSNumber) { } func session(_ session: QBRTCBaseSession, didChangeRconnectionState state: QBRTCReconnectionState, forUser userID: NSNumber) { } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // CallViewController.m @interface CallViewController: UIViewController<QBRTCClientDelegate> @end @implementation CallViewController // MARK: - Life cycle - (void)viewDidLoad { [super viewDidLoad]; [[QBRTCClient instance] addDelegate:self]; } // MARK: - QBRTCClientDelegate - (void)didReceiveNewSession:(QBRTCSession *)session userInfo:(NSDictionary *)userInfo { } - (void)session:(QBRTCSession *)session userDidNotRespond:(NSNumber *)userID { } - (void)session:(QBRTCSession *)session rejectedByUser:(NSNumber *)userID userInfo:(nullable NSDictionary <NSString *, NSString *> *)userInfo { } - (void)session:(QBRTCSession *)session acceptedByUser:(NSNumber *)userID userInfo:(nullable NSDictionary <NSString *, NSString *> *)userInfo { } - (void)session:(QBRTCSession *)session hungUpByUser:(NSNumber *)userID userInfo:(NSDictionary<NSString *,NSString *> *)userInfo { } - (void)sessionDidClose:(QBRTCSession *)session { } // MARK: - QBRTCBaseClientDelegate - (void)session:(__kindof QBRTCBaseSession *)session updatedStatsReport:(QBRTCStatsReport *)report forUserID:(NSNumber *)userID { } - (void)session:(__kindof QBRTCBaseSession *)session didChangeState:(QBRTCSessionState)state { } - (void)session:(__kindof QBRTCBaseSession *)session receivedRemoteAudioTrack:(QBRTCAudioTrack *)audioTrack fromUser:(NSNumber *)userID { } - (void)session:(__kindof QBRTCBaseSession *)session receivedRemoteVideoTrack:(QBRTCVideoTrack *)videoTrack fromUser:(NSNumber *)userID { } - (void)session:(__kindof QBRTCBaseSession *)session connectionClosedForUser:(NSNumber *)userID { } - (void)session:(__kindof QBRTCBaseSession *)session startedConnectingToUser:(NSNumber *)userID { } - (void)session:(__kindof QBRTCBaseSession *)session connectedToUser:(NSNumber *)userID { } - (void)session:(__kindof QBRTCBaseSession *)session disconnectedFromUser:(NSNumber *)userID { } - (void)session:(__kindof QBRTCBaseSession *)session connectionFailedForUser:(NSNumber *)userID { } - (void)session:(__kindof QBRTCBaseSession *)session didChangeConnectionState:(QBRTCConnectionState)state forUser:(nonnull NSNumber *)userID { } - (void)session:(__kindof QBRTCBaseSession *)session didChangeRconnectionState:(QBRTCReconnectionState)state forUser:(NSNumber *)userID { } ``` </Tab> </Tabs> Go to the [Resources](#resources) section to see a sequence diagram for a regular call workflow. ### Call session states Each call session has its own state. You can always access the current state by simply using the `QBRTCSession` property. <Tabs> <Tab title="Swift"> ```Swift theme={null} let sessionState = self.session.state ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCSessionState sessionState = self.session.state; ``` </Tab> </Tabs> You can also receive a live-time call session state. <Tabs> <Tab title="Swift"> ```Swift theme={null} func session(_ session: QBRTCBaseSession, didChange state: QBRTCConnectionState) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)session:(QBRTCBaseSession *)session didChangeState:(QBRTCConnectionState)state { } ``` </Tab> </Tabs> The following table lists all supported call session states: | State | Description | | --------------------------- | -------------------------------------------------------------------- | | QBRTCSessionStateNew | A call session was successfully created and ready for the next step. | | QBRTCSessionStatePending | A call session is in a pending state for other actions to occur. | | QBRTCSessionStateConnecting | A call session is in the progress of establishing a connection. | | QBRTCSessionStateConnected | A call session was successfully established. | | QBRTCSessionStateClosed | A call session has been closed. | ### Call reconnection states <Note> **Note** Since version 2.8.0 Quickblox-WebRTC SDK supports reconnection functional. </Note> You can receive a live-time call reconnection state. <Tabs> <Tab title="Swift"> ```Swift theme={null} func session(_ session: QBRTCBaseSession, didChangeRconnectionState state: QBRTCReconnectionState, forUser userID: NSNumber) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)session:(__kindof QBRTCBaseSession *)session didChangeRconnectionState:(QBRTCReconnectionState)state forUser:(NSNumber *)userID { } ``` </Tab> </Tabs> <Warning> Doesn’t invoke in conference. </Warning> The following table lists all supported call reconnection states: | State | Description | | ---------------------------------- | -------------------------------------------------------------------------------- | | QBRTCReconnectionStateReconnecting | A connection with opponent in reconnection progress after ICE connection failed. | | QBRTCReconnectionStateReconnected | A connection with opponent was successfully established. | | QBRTCReconnectionStateFailed | A connection with opponent isn’t restored by disconnect time interval. | You can also increase disconnect timeout. By default the time is set to 30 seconds. Minimal time is 10 seconds. It’s time while users can have availability to reconnect. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCConfig.setDisconnectTimeInterval(20) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCConfig setDisconnectTimeInterval:20]; ``` </Tab> </Tabs> ### Peer connection states Each peer connection has its own state. By default, you can access that state by calling this method from `QBRTCSession`. <Tabs> <Tab title="Swift"> ```Swift theme={null} let userID = 20450 as NSNumber // user with ID 20450 let connectionState = self.session.connectionState(forUser: userID) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSNumber *userID = @(20450); // user with ID 20450 QBRTCConnectionState connectionState = [self.session connectionStateForUser:userID]; ``` </Tab> </Tabs> You can also receive a real-time connection state. <Tabs> <Tab title="Swift"> ```Swift theme={null} func session(_ session: QBRTCSession, didChange state: QBRTCConnectionState, forUser userID: NSNumber) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)session:(QBRTCSession *)session didChangeConnectionState:(QBRTCConnectionState)state forUser:(NSNumber *)userID { } ``` </Tab> </Tabs> The following table lists all supported peer connection states: | State | Description | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | QBRTCConnectionUnknown | A connection state is unknown; this can occur when none of the other states are fit for the current situation. | | QBRTCConnectionStateNew | A peer connection has been created and hasn't done any networking yet. | | QBRTCConnectionStatePending | A connection is in a pending state for other actions to occur. | | QBRTCConnectionStateConnecting | One or more of the ICE transports are currently in the process of establishing a connection. | | QBRTCConnectionStateChecking | The ICE agent has been given one or more remote candidates and is checking pairs of local and remote candidates against one another to try to find a compatible match, but has not yet found a pair which will allow the peer connection to be made. It is possible that gathering of candidates is also still underway. | | QBRTCConnectionStateConnected | A usable pairing of local and remote candidates has been found for all components of the connection, and the connection has been established. | | QBRTCConnectionStateDisconnected | A peer has been disconnected from the call session. But the call session is still open and the peer can be reconnected to the session. | | QBRTCConnectionStateDisconnectTimeout | The peer connection was disconnected by the timeout. | | QBRTCConnectionStateClosed | A peer connection was closed. But the call session can still be open because there can several peer connections in a single call session. The ICE agent for this peer connection has shut down and is no longer handling requests. | | QBRTCConnectionStateCount | The ICE connection reached max numbers. | | QBRTCConnectionStateNoAnswer | The connection did not receive an answer from the remote peer. | | QBRTCConnectionStateRejected | The connection was rejected by the remote peer. | | QBRTCConnectionStateHangUp | The connection was hung up by the remote peer. | | QBRTCConnectionStateFailed | One or more of the ICE transports on the connection is in the failed state. This can occur in different circumstances, for example, bad network, etc. | ## Resources A regular call workflow. <Frame> <img alt="iOS SDK Initiate, Accept, Hang Up" /> </Frame> # Advanced Source: https://docs.quickblox.com/sdks/ios-video-calling-advanced Learn how to mute audio, disable video, switch camera, share your screen, configure media settings, etc. ## Mute audio Mute the audio by calling `localMediaStream.audioTrack.enabled()` method. Using this method, we can tell SDK to send/not send audio data either from a local or remote peer in the specified call session. <Tabs> <Tab title="Swift"> ```Swift theme={null} self.session?.localMediaStream.audioTrack.enabled = !self.session?.localMediaStream.audioTrack.enabled ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} self.session.localMediaStream.audioTrack.enabled ^= 1; ``` </Tab> </Tabs> ## Mute remote audio You can always get remote audio tracks for a specific user ID in the call using the below-specified `QBRTCSession` methods (assuming that they are existent). <Tabs> <Tab title="Swift"> ```Swift theme={null} let remoteAudioTrack = self.session?.remoteAudioTrack(withUserID: 24450) // audio track for user 24450 ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCAudioTrack *remoteAudioTrack = [self.session remoteAudioTrackWithUserID:@(24450)]; // audio track for user 24450 ``` </Tab> </Tabs> You can also mute remote media tracks on your side by changing the value of **enabled** property for a specific **remote media track**. <Tabs> <Tab title="Swift"> ```Swift theme={null} remoteAudioTrack.enabled = false ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} remoteAudioTrack.enabled = NO; ``` </Tab> </Tabs> ## Disable video Turn off/turn on the video by calling `localMediaStream.videoTrack.enabled()`. Using this method, we can tell SDK not to send video data either from a local or remote peer in the specified call session. <Tabs> <Tab title="Swift"> ```Swift theme={null} self.session?.localMediaStream.videoTrack.enabled = !self.session?.localMediaStream.videoTrack.enabled ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} self.session.localMediaStream.videoTrack.enabled ^=1; ``` </Tab> </Tabs> <Note> Due to [webrtc restrictions](https://www.w3.org/TR/webrtc/) black frames will be placed into stream content if video is disabled. </Note> ## Switch camera You can switch the video camera during a call. (Default: **front camera**) <Tabs> <Tab title="Swift"> ```Swift theme={null} // to change some time after, for example, at the moment of call let position = self.videoCapture?.position let newPosition = position == AVCaptureDevice.Position.Front ? AVCaptureDevice.Position.Back : AVCaptureDevice.Position.Front // check whether videoCapture has or has not camera position // for example, some iPods do not have front camera if self.videoCapture?.hasCameraForPosition(newPosition) { self.videoCapture?.position = newPosition } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // to change some time after, for example, at the moment of call AVCaptureDevicePosition position = self.videoCapture.position; AVCaptureDevicePosition newPosition = position == AVCaptureDevicePositionBack ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack; // check whether videoCapture has or has not camera position // for example, some iPods do not have front camera if ([self.videoCapture hasCameraForPosition:newPosition]) { self.videoCapture.position = newPosition; } ``` </Tab> </Tabs> ## Manage audio session QuickbloxWebRTC has its own audio session management which you need to use. It's located in the `QBRTCAudioSession` class. This class is represented as singleton and you can always access a shared session by calling the `instance()` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} let audioSession = QBRTCAudioSession.instance() ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCAudioSession *audioSession = [QBRTCAudioSession instance]; ``` </Tab> </Tabs> See the `QBRTCAudioSession` class header for more information. ### Configure You can configure an audio session using the `setConfiguration()` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} let audioSession = QBRTCAudioSession.instance() let configuration = QBRTCAudioSessionConfiguration() configuration.categoryOptions.insert(.duckOthers) // adding blutetooth support configuration.categoryOptions.insert(.allowBluetooth) configuration.categoryOptions.insert(.allowBluetoothA2DP) // adding airplay support configuration.categoryOptions.insert(.allowAirPlay) if hasVideo == true { // setting mode to video chat to enable airplay audio and speaker only configuration.mode = AVAudioSession.Mode.videoChat.rawValue } audioSession.setConfiguration(configuration) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCAudioSession *audioSession = [QBRTCAudioSession instance]; QBRTCAudioSessionConfiguration *configuration = [[QBRTCAudioSessionConfiguration alloc] init]; configuration.categoryOptions |= AVAudioSessionCategoryOptionDuckOthers; // adding blutetooth support configuration.categoryOptions |= AVAudioSessionCategoryOptionAllowBluetooth; configuration.categoryOptions |= AVAudioSessionCategoryOptionAllowBluetoothA2DP; // adding airplay support configuration.categoryOptions |= AVAudioSessionCategoryOptionAllowAirPlay; if (hasVideo) { configuration.mode = AVAudioSessionModeVideoChat; } [audioSession setConfiguration:configuration]; ``` </Tab> </Tabs> The `setConfiguration()` method accepts the `configuration` argument of the object type with the following fields: | Field | Required | Description | | --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | categoryOptions | no | Audio session category options allow to tailor the behavior of the active audio session category. Default: **allowBluetooth**. See [Apple documentation](https://developer.apple.com/documentation/avfaudio/avaudiosession/categoryoptions) to learn about supported values. | | mode | no | Audio session mode allows to assign specialized behavior to an audio session category. Default: **videoChat**. See [Apple documentation](https://developer.apple.com/documentation/avfaudio/avaudiosession/mode) to learn about supported values. | ### Activate Activate an audio session before every call. <Tabs> <Tab title="Swift"> ```Swift theme={null} let audioSession = QBRTCAudioSession.instance() let isActive = true audioSession.setActive(isActive) // or activate with configuration let configuration = QBRTCAudioSessionConfiguration() audioSession.setConfiguration(configuration, active: isActive) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCAudioSession *audioSession = [QBRTCAudioSession instance]; BOOL isActive = YES; [audioSession setActive:isActive]; // or activate with configuration QBRTCAudioSessionConfiguration *configuration = [[QBRTCAudioSessionConfiguration alloc] init]; [audioSession setConfiguration:configuration active:isActive]; ``` </Tab> </Tabs> The `setActive()`, method accepts the following argument: | Agrument | Required | Description | | -------- | -------- | ------------------------------------------------------------- | | isActive | yes | Boolean paramater. Set the true to activate an audio session. | The `setConfiguration()` accepts the following arguments: | Argument | Required | Description | | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | configuration | yes | Audio session configuration object. You can set the configuration object fields. See [this section](/sdks/ios-video-calling-advanced#configuration) to learn how to set audio session configuration. | | isActive | yes | Boolean paramater. Set the true to activate an audio session. | ### Deactivate Deactivate an audio session after the call ends. <Tabs> <Tab title="Swift"> ```Swift theme={null} let isActive = false QBRTCAudioSession.instance().setActive(isActive) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} BOOL isActive = NO; [QBRTCAudioSession.instance setActive:isActive]; ``` </Tab> </Tabs> | Argument | Required | Description | | -------- | -------- | ------------------------------------------------------------- | | isActive | yes | Boolean paramater. Set the true to activate an audio session. | ### Set audio output You can output audio either from the receiver unless you set the `AVAudioSessionModeVideoChat` mode or speaker. <Tabs> <Tab title="Swift"> ```Swift theme={null} let audioSession = QBRTCAudioSession.instance() // setting audio through receiver let receiver: AVAudioSession.PortOverride = .none audioSession.overrideOutputAudioPort(receiver) // setting audio through speaker let speaker: AVAudioSession.PortOverride = .speaker audioSession.overrideOutputAudioPort(speaker) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCAudioSession *audioSession = [QBRTCAudioSession instance]; // setting audio through receiver AVAudioSessionPortOverride receiver = AVAudioSessionPortOverrideNone; audioSession.overrideOutputAudioPort(receiver) // setting audio through speaker AVAudioSessionPortOverride speaker = AVAudioSessionPortOverrideSpeaker; [audioSession overrideOutputAudioPort:speaker]; ``` </Tab> </Tabs> | Argument | Required | Descriotion | | ---------------- | -------- | ------------------------------------- | | receiver/speaker | yes | State for the current audio category. | ## Screen sharing Screen sharing allows you to share information from your application to all of your opponents. It gives you the ability to promote your product, share a screen with formulas to students, distribute podcasts, share video/audio/photo moments of your life in real-time all over the world. To implement this feature in your application, we give you the ability to create custom **video capture**. **Video capture** is a base class you should inherit from in order to send frames to your opponents. There are two ways to implement this feature in your application. <Note> Due to Apple iOS restrictions, screen sharing feature works **only** within the app it is used in. </Note> **1. iOS 11 and newer**. With iOS 11, Apple has introduced a new way to capture your in-app screen using [ReplayKit's RPScreenRecorder](https://developer.apple.com/documentation/replaykit/rpscreenrecorder) class. This is the most optimal way to share screen and requires minimum resources as this is handled by iOS itself. However, the minimum requirement is iOS 11. <Tabs> <Tab title="Swift"> ```Swift theme={null} if #available(iOS 11.0, *) { self.screenCapture = QBRTCVideoCapture() RPScreenRecorder.shared().startCapture(handler: { (sampleBuffer, type, error) in switch type { case .video : let source = CMSampleBufferGetImageBuffer(sampleBuffer) let videoFrame = QBRTCVideoFrame(pixelBuffer: source, videoRotation: ._0) self.screenCapture.adaptOutputFormat(toWidth: UInt(UIScreen.main.bounds.width), height: UInt(UIScreen.main.bounds.height), fps: 30) self.screenCapture.send(videoFrame) break default: break } }) { (error) in if error != nil { print(error) } } } self.session?.localMediaStream.videoTrack.videoCapture = self.screenCapture ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} if ([UIDevice currentDevice].systemVersion.integerValue >= 11) { self.screenCapture = [[QBRTCVideoCapture alloc] init]; [RPScreenRecorder.sharedRecorder startCaptureWithHandler:^(CMSampleBufferRef _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) { switch (bufferType) { case RPSampleBufferTypeVideo: { CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); QBRTCVideoFrame *videoFrame = [[QBRTCVideoFrame alloc] initWithPixelBuffer:buffer videoRotation:QBRTCVideoRotation_0]; [self.screenCapture adaptOutputFormatToWidth:(NSUInteger)[UIScreen mainScreen].bounds.size.width height:(NSUInteger)[UIScreen mainScreen].bounds.size.height fps:30]; [self.screenCapture sendVideoFrame:videoFrame]; break; } default: break; } } completionHandler:^(NSError * _Nullable error) { NSLog(@"error: %@", error); }]; self.session.localMediaStream.videoTrack.videoCapture = self.screenCapture; } ``` </Tab> </Tabs> The `self.screenCapture` should be a property of `QBRTCVideoCapture` class type. <Warning> **30 fps** is a **maximum** rate for WebRTC, even though `RPScreenRecorder` supports 60 fps, you must set it to **30 or lower**. </Warning> **2. iOS 10 and older**. This way is a little bit tricky and you will not be able to achieve resource-free **30 fps** screen sharing. The maximum fps rate we recommend here is **5** as this a resource-heavy operation. `QBRTCVideoCapture` class allows sending frames to your opponents. By inheriting this class you are able to provide custom logic to create frames, modify them, and then send to your opponents. Below you can find an example of how to implement a custom video capture and send frames to your opponents (this class is designed to share **5** screenshots per second). <Tabs> <Tab title="Swift"> ```Swift theme={null} import UIKit import QuickbloxWebRTC struct ScreenCaptureConstant { // * By default sending frames in screen share using BiPlanarFullRange pixel format type. // * You can also send them using ARGB by setting this constant to NO. static let isUseBiPlanarFormatTypeForShare = true } // * Class implements screen sharing and converting screenshots to destination format // * in order to send frames to your opponents class ScreenCapture: QBRTCVideoCapture { //MARK: - Properties private var view = UIView() private var displayLink = CADisplayLink() static let sharedGPUContextSharedContext: CIContext = { let options = [CIContextOption.priorityRequestLow: true] let sharedContext = CIContext(options: options) return sharedContext }() //MARK: - Life Cycle // * Initialize a video capturer view and start grabbing content of given view init(view: UIView) { super.init() self.view = view } private func sharedContext() -> CIContext { return ScreenCapture.sharedGPUContextSharedContext } //MARK: - Enter Background / Fofeground notifications @objc func willEnterForeground(_ note: Notification?) { displayLink.isPaused = false } @objc func didEnterBackground(_ note: Notification?) { displayLink.isPaused = true } //MARK: - Internal Methods func screenshot() -> UIImage? { let layer = view.layer UIGraphicsBeginImageContextWithOptions(layer.frame.size, true, 1); guard let context = UIGraphicsGetCurrentContext() else { return nil } layer.render(in:context) let screenshotImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return screenshotImage } @objc private func sendPixelBuffer(_ sender: CADisplayLink?) { guard let image = self.screenshot() else { return } videoQueue.async(execute: { [weak self] in guard let self = self else { return } let renderWidth = Int(image.size.width) let renderHeight = Int(image.size.height) var pixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange var pixelBufferAttributes = [kCVPixelBufferIOSurfacePropertiesKey: [:]] as CFDictionary if ScreenCaptureConstant.isUseBiPlanarFormatTypeForShare == false { pixelFormatType = kCVPixelFormatType_32ARGB pixelBufferAttributes = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanFalse, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanFalse] as CFDictionary } var pixelBuffer: CVPixelBuffer? let status = CVPixelBufferCreate(kCFAllocatorDefault, renderWidth, renderHeight, pixelFormatType, pixelBufferAttributes, &pixelBuffer) if status != kCVReturnSuccess { return } guard let buffer = pixelBuffer else { return } CVPixelBufferLockBaseAddress(buffer, CVPixelBufferLockFlags(rawValue: 0)) if let renderImage = CIImage(image: image), ScreenCaptureConstant.isUseBiPlanarFormatTypeForShare == true { self.sharedContext().render(renderImage, to: buffer) } else if let cgImage = image.cgImage { let pxdata = CVPixelBufferGetBaseAddress(buffer) let rgbColorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue let context = CGContext(data: pxdata, width: renderWidth, height: renderHeight, bitsPerComponent: 8, bytesPerRow: renderWidth * 4, space: rgbColorSpace, bitmapInfo: bitmapInfo) let rect = CGRect(x: 0, y: 0, width: renderWidth, height: renderHeight) context?.draw(cgImage, in: rect) } CVPixelBufferUnlockBaseAddress(buffer, CVPixelBufferLockFlags(rawValue: 0)) let videoFrame = QBRTCVideoFrame(pixelBuffer: buffer, videoRotation: QBRTCVideoRotation._0) self.send(videoFrame) }) } // MARK: - <QBRTCVideoCapture> override func didSet(to videoTrack: QBRTCLocalVideoTrack?) { super.didSet(to: videoTrack) displayLink = CADisplayLink(target: self, selector: #selector(sendPixelBuffer(_:))) displayLink.add(to: .main, forMode: .common) displayLink.preferredFramesPerSecond = 12 //5 fps NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil) } override func didRemove(from videoTrack: QBRTCLocalVideoTrack?) { super.didRemove(from: videoTrack) displayLink.isPaused = true displayLink.remove(from: .main, forMode: .common) NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIApplication.didEnterBackgroundNotification, object: nil) } } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} #import "ScreenCapture.h" // * By default sending frames in screen share using BiPlanarFullRange pixel format type. // * You can also send them using ARGB by setting this constant to NO. static const BOOL kQBRTCUseBiPlanarFormatTypeForShare = YES; @interface ScreenCapture() @property (weak, nonatomic) UIView * view; @property (strong, nonatomic) CADisplayLink *displayLink; @end @implementation ScreenCapture - (instancetype)initWithView:(UIView *)view { self = [super init]; if (self) { _view = view; } return self; } #pragma mark - Enter BG / FG notifications - (void)willEnterForeground:(NSNotification *)note { self.displayLink.paused = NO; } - (void)didEnterBackground:(NSNotification *)note { self.displayLink.paused = YES; } #pragma mark - - (UIImage *)screenshot { UIGraphicsBeginImageContextWithOptions(_view.frame.size, YES, 1); [_view drawViewHierarchyInRect:_view.bounds afterScreenUpdates:NO]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } - (CIContext *)qb_sharedGPUContext { static CIContext *sharedContext; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSDictionary *options = @{kCIContextPriorityRequestLow: @YES}; sharedContext = [CIContext contextWithOptions:options]; }); return sharedContext; } - (void)sendPixelBuffer:(CADisplayLink *)sender { __weak __typeof(self)weakSelf = self; dispatch_async(self.videoQueue, ^{ @autoreleasepool { UIImage *image = [weakSelf screenshot]; int renderWidth = image.size.width; int renderHeight = image.size.height; CVPixelBufferRef buffer = NULL; OSType pixelFormatType; CFDictionaryRef pixelBufferAttributes = NULL; if (kQBRTCUseBiPlanarFormatTypeForShare) { pixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; pixelBufferAttributes = (__bridge CFDictionaryRef) @{(__bridge NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{}}; } else { pixelFormatType = kCVPixelFormatType_32ARGB; pixelBufferAttributes = (__bridge CFDictionaryRef) @{(NSString *)kCVPixelBufferCGImageCompatibilityKey : @NO,(NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey : @NO}; } CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, renderWidth, renderHeight, pixelFormatType, pixelBufferAttributes, &buffer); if (status == kCVReturnSuccess && buffer != NULL) { CVPixelBufferLockBaseAddress(buffer, 0); if (kQBRTCUseBiPlanarFormatTypeForShare) { CIImage *rImage = [[CIImage alloc] initWithImage:image]; [weakSelf.qb_sharedGPUContext render:rImage toCVPixelBuffer:buffer]; } else { void *pxdata = CVPixelBufferGetBaseAddress(buffer); CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); uint32_t bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst; CGContextRef context = CGBitmapContextCreate(pxdata, renderWidth, renderHeight, 8, renderWidth * 4, rgbColorSpace, bitmapInfo); CGContextDrawImage(context, CGRectMake(0, 0, renderWidth, renderHeight), [image CGImage]); CGColorSpaceRelease(rgbColorSpace); CGContextRelease(context); } CVPixelBufferUnlockBaseAddress(buffer, 0); QBRTCVideoFrame *videoFrame = [[QBRTCVideoFrame alloc] initWithPixelBuffer:buffer videoRotation:QBRTCVideoRotation_0]; [super sendVideoFrame:videoFrame]; } CVPixelBufferRelease(buffer); } }); } #pragma mark - <QBRTCVideoCapture> - (void)didSetToVideoTrack:(QBRTCLocalVideoTrack *)videoTrack { [super didSetToVideoTrack:videoTrack]; self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(sendPixelBuffer:)]; [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; self.displayLink.preferredFramesPerSecond = 12; //5 fps [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; } - (void)didRemoveFromVideoTrack:(QBRTCLocalVideoTrack *)videoTrack { [super didRemoveFromVideoTrack:videoTrack]; self.displayLink.paused = YES; [self.displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; self.displayLink = nil; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; } @end ``` </Tab> </Tabs> ## WebRTC stats reporting Stats reporting is an insanely powerful tool which can help to debug a call if there are any problems with it (for example, lags, missing audio/video etc). To enable stats report you should first set stats reporting frequency using `setStatsReportTimeInterval()` method below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCConfig.setStatsReportTimeInterval(5) // receive stats report every 5 seconds ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCConfig setStatsReportTimeInterval:5]; // receive stats report every 5 seconds ``` </Tab> </Tabs> Now you will be able to receive a client delegate callback and perform operations with `QBRTCStatsReport` instance for the current period of time. <Tabs> <Tab title="Swift"> ```Swift theme={null} func session(_ session: QBRTCBaseSession, updatedStatsReport report: QBRTCStatsReport, forUserID userID: NSNumber) { print(report.statsString()) } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)session:(QBRTCBaseSession *)session updatedStatsReport:(QBRTCStatsReport *)report forUserID:(NSNumber *)userID { NSLog(@"%@", [report statsString]); } ``` </Tab> </Tabs> By calling `statsString()`, you will receive a generic report string, which will contain the most useful data to debug a call, for example: ```Logs Logs theme={null} CN 565ms | local->local/udp | (s)248Kbps | (r)869Kbps VS (input) 640x480@30fps | (sent) 640x480@30fps VS (enc) 279Kbps/260Kbps | (sent) 200Kbps/292Kbps | 8ms | H264 AvgQP (past 30 encoded frames) = 36 VR (recv) 640x480@26fps | (decoded)27 | (output)27fps | 827Kbps/0bps | 4ms AS 38Kbps | opus AR 37Kbps | opus | 168ms | (expandrate)0.190002 Packets lost: VS 17 | VR 0 | AS 3 | AR 0 ``` <Note> * `CN` Connection info.\ \*`VS` Video sent. * `VR` Video received. * `AvgQP` Average quantization parameter (only valid for video; it is calculated as a fraction of the current delta sum over the current delta of encoded frames; low value corresponds with good quality; the range of the value per frame is defined by the codec being used). * `AS` Audio sent. * `AR` Audio received. </Note> You can also use stats reporting to see who is currently talking in a group call. You must use `audioReceivedOutputLevel` for that. Take a look at the `QBRTCStatsReport` header file to see all of the other stats properties that can be useful for you. ## Calling offline users (CallKit) Before starting you need to configure APNs and/or VoIP push certificate in your admin panel. Use [this guide](/sdks/ios-push-notifications) to add push notifications feature to your QuickBlox application. ### Generic push notifications You can send a regular push notification to users you call, this will notify them about your call (if they have subscribed to push notifications in their app, see [Push notifications guide](/sdks/ios-push-notifications)). <Tabs> <Tab title="Swift"> ```Swift theme={null} let currentUserFullName = QBSession.current.currentUser?.fullName let text = "\(currentUserFullName) is calling you" let users = self.session?.opponentsIDs.map({ $0.stringValue }).joined(separator: ",") QBRequest.sendPush(withText: text, toUsers:users!, successBlock: { (response, event) in print("Push sent!") }, errorBlock: { (error) in print(error) }) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *currentUserFullName = [[[QBSession currentSession] currentUser] fullName]; NSString *text = [NSString stringWithFormat:@"%@ is calling you", currentUserFullName]; NSString *users = [self.session.opponentsIDs componentsJoinedByString:@","]; [QBRequest sendPushWithText:text toUsers:users successBlock:^(QBResponse * _Nonnull response, NSArray<QBMEvent *> * _Nullable events) { NSLog(@"Push sent!"); } errorBlock:^(QBError * _Nonnull error) { NSLog(@"Can not send push: %@", error); }]; ``` </Tab> </Tabs> ### Apple CallKit using VoIP push notifications QuickbloxWebRTC fully supports [Apple CallKit](https://developer.apple.com/documentation/callkit). In this block, we will guide you through the most important things you need to know when integrating CallKit into your application. To learn more about this process, review the above-specified link. **Project preparations** In your Xcode project, make sure that your app supports Voice over IP services. For that open your Info.plist and make sure you have a specific line in Required background modes array: <img alt="1159" /> Now you are ready to integrate CallKit methods using [Apple guide here](https://developer.apple.com/documentation/callkit). **Managing audio session** CallKit requires you to manage Audio session by yourself. Use `QBRTCAudioSession ` instance for that task. See [Manage audio session](/sdks/ios-video-calling-advanced#manage-audio-session) section for more information. **Initializing audio session** You must initialize audio session every time before you call the `reportNewIncomingCall(with:update:completion:)` method of `CXProvider` showing the incoming call screen. Before initializing the audio session, set `useManualAudio` property value to `YES`. This will not activate WebRTC audio before iOS allows it. You will need to activate audio manually later. See [Manage audio session](/sdks/ios-video-calling-advanced#manage-audio-session) section for more information. **Managing audio session activations** `CXProviderDelegate` has 2 delegate methods that you must conform to: 1. `provider(_:didActivate:)` 2. `provider(_:didDeactivate:)` Using `QBRTCAudioSessionActivationDelegate` protocol of `QBRTCAudioSession` class, you need to notify that the session was activated outside of it. The `provider(_:didActivate:)` is a `CXProviderDelegate` where we need to activate our audio manually. Set `audioEnabled` property of `QBRTCAudioSession ` class in here, to enable WebRTC audio as iOS has pushed audio session priority of our app to the top. <Tabs> <Tab title="Swift"> ```Swift theme={null} func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { let callAudioSession = QBRTCAudioSession.instance() callAudioSession.audioSessionDidActivate(audioSession) // enabling audio now callAudioSession.isAudioEnabled = true } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)provider:(CXProvider *)__unused provider didActivateAudioSession:(AVAudioSession *)audioSession { QBRTCAudioSession *callAudioSession = [QBRTCAudioSession instance]; [callAudioSession audioSessionDidActivate:audioSession]; // enabling audio now callAudioSession.audioEnabled = YES; } ``` </Tab> </Tabs> **Deinitializing audio session** Deinitialize audio session every time `CXProvider` deactivates it in `provider(_:didDeactivate)` of `CXProviderDelegate`. Deinitializing audio session earlier would lead to issues with the audio session. <Tabs> <Tab title="Swift"> ```Swift theme={null} func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { if QBRTCAudioSession.instance().isActive == false { return } QBRTCAudioSession.instance().audioSessionDidDeactivate(audioSession) } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} - (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSession *)audioSession { if (QBRTCAudioSession.instance.isActive == NO) { return; } [[QBRTCAudioSession instance] audioSessionDidDeactivate:audioSession]; } ``` </Tab> </Tabs> If you also have the deinitialization code of `QBRTCAudioSession` somewhere else in your app, you can check and ignore it with `QBRTCAudioSessionActivationDelegate` `audioSessionIsActivatedOutside()` method. By this, you will know for sure that **CallKit** is in charge of your audio session. Do not forget to restore `QBRTCAudioSession` properties to default values in `provider(_:perform:)` method of `CXProviderDelegate`. <Tabs> <Tab title="Swift"> ```Swift theme={null} // The deinitialization code of `QBRTCAudioSession` somewhere else in your app private func closeCall() { QBRTCAudioSession.instance().setActive(false) } // MARK: - CXProviderDelegate protocol func provider(_ provider: CXProvider, perform action: CXEndCallAction) { QBRTCAudioSession.instance().isAudioEnabled = false QBRTCAudioSession.instance().useManualAudio = false if (QBRTCAudioSession.instance().isActive) { QBRTCAudioSession.instance().setActive(false) } action.fulfill() } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // The deinitialization code of `QBRTCAudioSession` somewhere else in your app - (void)closeCall { if (QBRTCAudioSession.instance.isActive == NO) { return; } [QBRTCAudioSession.instance setActive:NO]; } // MARK: - CXProviderDelegate protocol - (void)provider:(CXProvider *)__unused provider performEndCallAction:(CXEndCallAction *)action { QQBRTCAudioSession.instance.audioEnabled = NO; QBRTCAudioSession.instance.useManualAudio = NO; [QBRTCAudioSession.instance setActive:NO]; [action fulfill]; } ``` </Tab> </Tabs> ## General settings You can change different settings for your calls using `QBRTCConfig` class. All of them are listed below. ### Answer time interval If an opponent hasn't answered you within an answer time interval, then `session(_:userDidNotRespond:)` and `session(_:connectionClosedForUser:)` delegate methods will be called. The answer time interval shows how much time an opponent has to answer your call. Set the answer time interval using the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCConfig.setAnswerTimeInterval(45) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCConfig setAnswerTimeInterval:45]; ``` </Tab> </Tabs> <Note> * **By default**, the answer time interval is 45 seconds. * The **maximum** values is 60 seconds. * The **minimum** value is 10 seconds. </Note> ### Dialing time interval Dialing time interval indicates how often to notify your opponents about your call. Set the dialing time interval using the code snippet below. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCConfig.setDialingTimeInterval(5) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCConfig setDialingTimeInterval:5]; ``` </Tab> </Tabs> <Note> * **By default**, the dialing time interval is 5 seconds. * The **minimum** value is 3 seconds. </Note> ### Datagram Transport Layer Security Datagram Transport Layer Security (DTLS) is used to provide communications privacy for datagram protocols. This fosters a secure signaling channel that cannot be tampered with. In other words, no eavesdropping or message forgery can occur on a DTLS encrypted connection. <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCConfig.setDTLSEnabled(true) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCConfig setDTLSEnabled:YES]; ``` </Tab> </Tabs> <Note> **By default**, DTLS is enabled. </Note> ### Custom ICE servers You can customize a list of ICE servers. **By default**, WebRTC module will use internal ICE servers that are usually enough, but you can always set your own. WebRTC engine will choose the TURN relay with the lowest round-trip time. Thus, setting multiple TURN servers allows your application to scale-up in terms of bandwidth and number of users. Review our [Setup](/sdks/ios-setup#custom-ice-servers) guide to learn how to configure custom ICE servers. ## Media settings You can configure a variety of media settings such as video/audio codecs, camera resolution, etc. ### Video codecs You can choose video codecs from available values: * `QBRTCVideoCodecVP8` - VP8 video codec * `QBRTCVideoCodecH264Baseline` - h264 baseline video codec * `QBRTCVideoCodecH264High` - h264 high video codec **VP8** is a software-supported video codec on Apple devices, which means it is the most demanding among all available ones. **H264** is a hardware-supported video codec, which means that it is the most optimal one for use when performing video codec. Using hardware acceleration, you can always guarantee the best performance when encoding and decoding video frames. There are two options available: * **baseline** is the most suited one for video calls as it has a low cost (default value). * **high** is mainly suited for broadcast to ensure you have the best picture possible. Takes more resources to encode/decode for the same resolution you set. <Tabs> <Tab title="Swift"> ```Swift theme={null} let mediaStreamConfiguration = QBRTCMediaStreamConfiguration.default() mediaStreamConfiguration = .h264Baseline ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCMediaStreamConfiguration defaultConfiguration].videoCodec = QBRTCVideoCodecH264Baseline; ``` </Tab> </Tabs> <Note> This will set your preferred codec as WebRTC will always choose the most suitable one for both sides in a call through negotiations. </Note> ### Video quality Video quality depends on the hardware you use. It also depends on the network you use and how many connections you have. For multi-calls, set lower video quality. For 1 to 1 calls, you can set a higher quality. You can use our `formatsWithPosition()` method in order to get all supported formats for a current device. <Tabs> <Tab title="Swift"> ```Swift theme={null} let cameraPosition:AVCaptureDevice.Position = .front // front or back let videoFormats = QBRTCCameraCapture.formats(with: cameraPosition) // Array of possible QBRTCVideoFormat video formats for requested device for cameraPosition ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} AVCaptureDevicePosition cameraPosition = AVCaptureDevicePositionFront; // front or back NSArray<QBRTCVideoFormat *> *formats = [QBRTCCameraCapture formatsWithPosition:cameraPosition]; // Array of possible QBRTCVideoFormat video formats for requested device for cameraPosition ``` </Tab> </Tabs> WebRTC has auto-scaling of video resolution and quality to keep network connection active. To get the best quality and performance, you should use the h264-baseline codec as your preferred one. 1. If some opponent user does not support h264, then automatically VP8 will be used. 2. If both caller and callee have h264 support, then h264 will be used. ### Camera resolution It's possible to set custom video resolution using `QBRTCVideoFormat`. <Tabs> <Tab title="Swift"> ```Swift theme={null} let customVideoFormat: QBRTCVideoFormat = QBRTCVideoFormat.init(width: 950, height: 540, frameRate: 30, pixelFormat: .format420f) // custom video format let cameraCapture = QBRTCCameraCapture(videoFormat: customVideoFormat, position: cameraPosition) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCVideoFormat *customVideoFormat = [QBRTCVideoFormat videoFormatWithWidth:950 height:540 frameRate:30 pixelFormat:QBRTCPixelFormat420f]; // custom video format QBRTCCameraCapture *cameraCapture = [[QBRTCCameraCapture alloc] initWithVideoFormat:customVideoFormat position:cameraPosition]; ``` </Tab> </Tabs> | Parameters | Description | | ----------- | ----------------------------------------------------- | | width | Video width. Default: **640**. | | height | Video hight. Default: **480**. | | frameRate | Video frames per second. Default: **30**. | | pixelFormat | Video pixel format. Default: **QBRTCPixelFormat420f** | You can also get a list of available resolution formats using `formats(with:)`. Set a needed one from the list using the snippet below, <Tabs> <Tab title="Swift"> ```Swift theme={null} var formats = QBRTCCameraCapture.formats(with: cameraPosition) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSArray<QBRTCVideoFormat *> *videoFormats = [QBRTCCameraCapture formatsWithPosition:cameraPosition]; // Array of possible QBRTCVideoFormat video formats for requested device ``` </Tab> </Tabs> ### Audio codecs You can choose audio codecs from available values: * `QBRTCAudioCodecOpus` * `QBRTCAudioCodecISAC` * `QBRTCAudioCodeciLBC` <Note> **By default**, `QBRTCAudioCodecOpus` is set. </Note> <Tabs> <Tab title="Swift"> ```Swift theme={null} let mediaStreamConfiguration = QBRTCMediaStreamConfiguration.default() mediaStreamConfiguration = .codecOpus ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCMediaStreamConfiguration defaultConfiguration].audioCodec = QBRTCAudioCodecOpus; ``` </Tab> </Tabs> **Opus** In the latest versions of Firefox and Chrome. This codec is used by default for encoding audio streams. This codec is relatively new (released in 2012). It implements lossy audio compression. Opus can be used for both low and high bitrates. * Supported bitrate: constant and variable, from 6 kbit/s to 510 kbit/s. * Supported sampling rates: from 8 kHz to 48 kHz. If you develop a Calls application that is supposed to work with high-quality audio, the **only** choice on audio codecs is OPUS. OPUS has the best quality, but it also requires a good internet connection. **iSAC** This codec was developed specifically for VoIP applications and audio streaming. * Supported bitrates: adaptive and variable. From 10 kbit/s to 52 kbit/s. * Supported sampling rates: 32 kHz. A good choice for the voice data, but not nearly as good as OPUS. **iLBC** This audio codec is well-known. It was released in 2004 and became part of the WebRTC project in 2011 when Google acquired Global IP Solutions (the company that developed iLIBC). When you have bad connection quality and low bandwidth, you definitely should try iLBC. It should be strong in such cases. * Supported bitrates: fixed bitrate. 15.2 kbit/s or 13.33 kbit/s * Supported sampling rate: 8 kHz. Thus, when you have a strong reliable and good internet connection, then use OPUS. If you use calls on 3g networks, use iSAC. If you still have problems, try iLBC. # Video Conference Source: https://docs.quickblox.com/sdks/ios-video-conference Learn how to add video conference calls to your app. <Warning> This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail:[enterprise@quickblox.com](mailto:enterprise@quickblox.com.). </Warning> QuickBlox provides a Multiparty Video Conferencing solution allowing to set up a video conference between 10-12 people. It is built on top of [WebRTC SFU](https://webrtcglossary.com/sfu/) technologies. Features supported: \- Video/Audio Conference with 10-12 people. \- Join/Rejoin video room functionality (like Skype). \- Mute/Unmute audio/video stream (own and opponents). \- Display bitrate. \- Switch video input device (camera). ## Conference endpoint `QBRTCConfig` class introduces new setting for Conference - conference endpoint. To set a specific conference endpoint use the following method: <Tabs> <Tab title="Swift"> ```Swift theme={null} QBRTCConfig.setConferenceEndpoint("MyConferenceEndpoint") ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} [QBRTCConfig setConferenceEndpoint:@"MyConferenceEndpoint"]; ``` </Tab> </Tabs> <Note> The endpoint should be a correct QuickBlox Conference server endpoint. </Note> Use this method to get a current conference endpoint. The default value is `nil`. <Tabs> <Tab title="Swift"> ```Swift theme={null} let conferenceEndpoint = QBRTCConfig.conferenceEndpoint() ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSString *conferenceEndpoint = QBRTCConfig.conferenceEndpoint; ``` </Tab> </Tabs> ## Conference client The conference module has its own client. **Conference client delegate** Conference client delegate is inherited from base client delegate and has all of its protocol methods implemented as well. **Base client delegate protocol methods** All protocol methods below have their own explanation inlined and are optional. <Tabs> <Tab title="Swift"> ```Swift theme={null} // * Called by timeout with updated stats report for user ID. // * // * @param session QBRTCSession instance // * @param report QBRTCStatsReport instance // * @param userID user ID // * // * @remark Configure time interval with // QBRTCConfig.setStatsReportTimeInterval(1.0). func session(_ session: QBRTCBaseSession, updatedStatsReport report: QBRTCStatsReport, forUserID userID: NSNumber) { } // * Called when session state has been changed. // * // * @param session QBRTCSession instance // * @param state session state // * // * @discussion Use this to track a session state. // As SDK 2.3 introduced states for session, // you can now manage your own states based on this. func session(_ session: QBRTCBaseSession, didChange state: QBRTCConnectionState, forUser userID: NSNumber) { } // * Called when received remote audio track from user. // * // * @param audioTrack QBRTCAudioTrack instance // * @param userID ID of user func session(_ session: QBRTCBaseSession, receivedRemoteAudioTrack audioTrack: QBRTCAudioTrack, fromUser userID: NSNumber) { } // * Called when received remote video track from user. // * // * @param videoTrack QBRTCVideoTrack instance // * @param userID ID of user func session(_ session: QBRTCBaseSession, receivedRemoteVideoTrack videoTrack: QBRTCVideoTrack, fromUser userID: NSNumber) { } // * Called when connection is closed for user. // * // * @param session QBRTCSession instance // * @param userID ID of user func session(_ session: QBRTCBaseSession, connectionClosedForUser userID: NSNumber) { } // * Called when connection is initiated with user. // * // * @param session QBRTCSession instance // * @param userID ID of user func session(_ session: QBRTCBaseSession, startedConnectingToUser userID: NSNumber) { } // * Called when connection is established with user. // * // * @param session QBRTCSession instance // * @param userID ID of user func session(_ session: QBRTCBaseSession, connectedToUser userID: NSNumber) { } // * Called when disconnected from user. // * // * @param session QBRTCSession instance // * @param userID ID of user func session(_ session: QBRTCBaseSession, disconnectedFromUser userID: NSNumber) { } // * Called when connection failed with user. // * // * @param session QBRTCSession instance // * @param userID ID of user func session(_ session: QBRTCBaseSession, connectionFailedForUser userID: NSNumber) { } // * Called when session connection state changed for a specific user. // * // * @param session QBRTCSession instance // * @param state state - @see QBRTCConnectionState // * @param userID ID of user func session(_ session: QBRTCBaseSession, didChange state: QBRTCConnectionState, forUser userID: NSNumber) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // * Called by timeout with updated stats report for user ID. // * // * @param session QBRTCSession instance // * @param report QBRTCStatsReport instance // * @param userID user ID // * // * @remark Configure time interval with // [QBRTCConfig setStatsReportTimeInterval:timeInterval]. - (void)session:(__kindof QBRTCBaseSession *)session updatedStatsReport:(QBRTCStatsReport *)report forUserID:(NSNumber *)userID { } // * Called when session state has been changed. // * // * @param session QBRTCSession instance // * @param state session state // * // * @discussion Use this to track a session state. // As SDK 2.3 introduced states for session, // you can now manage your own states based on this. - (void)session:(__kindof QBRTCBaseSession *)session didChangeState:(QBRTCSessionState)state { } // * Called when received remote audio track from user. // * // * @param audioTrack QBRTCAudioTrack instance // * @param userID ID of user - (void)session:(__kindof QBRTCBaseSession *)session receivedRemoteAudioTrack:(QBRTCAudioTrack *)audioTrack fromUser:(NSNumber *)userID { } // * Called when received remote video track from user. // * // * @param videoTrack QBRTCVideoTrack instance // * @param userID ID of user - (void)session:(__kindof QBRTCBaseSession *)session receivedRemoteVideoTrack:(QBRTCVideoTrack *)videoTrack fromUser:(NSNumber *)userID { } // * Called when connection is closed for user. // * // @param session QBRTCSession instance // @param userID ID of user - (void)session:(__kindof QBRTCBaseSession *)session connectionClosedForUser:(NSNumber *)userID { } // * Called when connection is initiated with user. // * // * @param session QBRTCSession instance // * @param userID ID of user - (void)session:(__kindof QBRTCBaseSession *)session startedConnectingToUser:(NSNumber *)userID { } // * Called when connection is established with user. // * // * @param session QBRTCSession instance // * @param userID ID of user - (void)session:(__kindof QBRTCBaseSession *)session connectedToUser:(NSNumber *)userID { } // * Called when disconnected from user. // * // * @param session QBRTCSession instance // * @param userID ID of user - (void)session:(__kindof QBRTCBaseSession *)session disconnectedFromUser:(NSNumber *)userID { } // * Called when connection failed with user. // * // * @param session QBRTCSession instance // * @param userID ID of user - (void)session:(__kindof QBRTCBaseSession *)session connectionFailedForUser:(NSNumber *)userID { } // * Called when session connection state changed for a specific user. // * // * @param session QBRTCSession instance // * @param state state - @see QBRTCConnectionState // * @param userID ID of user - (void)session:(__kindof QBRTCBaseSession *)session didChangeConnectionState:(QBRTCConnectionState)state forUser:(NSNumber *)userID { } ``` </Tab> </Tabs> **Conference client delegate protocol methods** All protocol methods below are conference client-specific, optional, and have their own explanation inlined. <Tabs> <Tab title="Swift"> ```Swift theme={null} // * Called when session was created on server. // * // * @param session QBRTCConferenceSession instance // * // * @discussion When this method is called, session // instance that was already created by QBRTCConferenceClient // * will be assigned valid session ID from server. // * // * @see QBRTCConferenceSession, QBRTCConferenceClient func didCreateNewSession(_ session: QBRTCConferenceSession?) { } // * Called when join to session is performed and // acknowledged by server. // * // * @param session QBRTCConferenceSession instance // * @param chatDialogID chat dialog ID // * @param publishersList array of user IDs, // that are currently publishers // * // * @see QBRTCConferenceSession func session(_ session: QBRTCConferenceSession?, didJoinChatDialogWithID chatDialogID: String?, publishersList: [NSNumber]) { } // * Called when new publisher did join. // * // * @param session QBRTCConferenceSession instance // * @param userID new publisher user ID // * // * @see QBRTCConferenceSession func session(_ session: QBRTCConferenceSession?, didReceiveNewPublisherWithUserID userID: NSNumber?) { } // * Called when publisher did leave. // * // * @param session QBRTCConferenceSession instance // * @param userID publisher that left user ID // * // * @see QBRTCConferenceSession func session(_ session: QBRTCConferenceSession?, publisherDidLeaveWithUserID userID: NSNumber?) { } // * Called when session did receive error from server. // * // * @param session QBRTCConferenceSession instance // * @param error received error from server // * // * @note Error doesn't necessarily means that session // is closed. Can be just a minor error that can be fixed/ignored. // * // * @see QBRTCConferenceSession func session(_ session: QBRTCConferenceSession!, didReceiveError error: Error!) { } // * Called when slowlink was received. // * // * @param session QBRTCConferenceSession instance // * @param uplink whether the issue is uplink or not // * @param nacks number of nacks // * // * @discussion this callback is triggered when serber // reports trouble either sending or receiving media on the // * specified connection, typically as a consequence of // too many NACKs received from/sent to the user in the last // * second: for instance, a slowLink with uplink=true // means you notified several missing packets from server, // * while uplink=false means server is not receiving // all your packets. // * // * @note useful to figure out when there are problems on // the media path (for example,, excessive loss), in order to // * possibly react accordingly (for example,, decrease the bitrate if // most of our packets are getting lost). // * // * @see QBRTCConferenceSession func session(_ session: QBRTCConferenceSession!, didReceiveSlowlinkWithUplink uplink: Bool, nacks: NSNumber!) { } // * Called when media receiving state was changed on server. // * // * @param session QBRTCConferenceSession instance // * @param mediaType media type // * @param receiving whether media is receiving by server // * // * @see QBRTCConferenceSession, QBRTCConferenceMediaType func session(_ session: QBRTCConferenceSession!, didChangeMediaStateWith mediaType: QBRTCConferenceMediaType, receiving: Bool) { } // * Session did initiate close request. // * // * @param session QBRTCConferenceSession instance // * // * @discussion 'sessionDidClose:withTimeout:' will be // called after server will close session with callback // * // * @see QBRTCConferenceSession func sessionWillClose(_ session: QBRTCConferenceSession?) { } // * Called when session was closed completely on server. // * // * @param session QBRTCConferenceSession instance // * @param timeout whether session was closed due to timeout on server // * // * @see QBRTCConferenceSession func sessionDidClose(_ session: QBRTCConferenceSession?, withTimeout timeout: Bool) { } ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // * Called when session was created on server. // * // * @param session QBRTCConferenceSession instance // * // * @discussion When this method is called, session // instance that was already created by QBRTCConferenceClient // * will be assigned valid session ID from server. // * // * @see QBRTCConferenceSession, QBRTCConferenceClient - (void)didCreateNewSession:(QBRTCConferenceSession *)session { } // * Called when join to session is performed and // acknowledged by server. // * // * @param session QBRTCConferenceSession instance // * @param chatDialogID chat dialog ID // * @param publishersList array of user IDs, // that are currently publishers // * // * @see QBRTCConferenceSession - (void)session:(QBRTCConferenceSession *)session didJoinChatDialogWithID:(NSString *)chatDialogID publishersList:(NSArray <NSNumber *> *)publishersLis { } // * Called when new publisher did join. // * // * @param session QBRTCConferenceSession instance // * @param userID new publisher user ID // * // * @see QBRTCConferenceSession - (void)session:(QBRTCConferenceSession *)session didReceiveNewPublisherWithUserID:(NSNumber *)userID { } // * Called when publisher did leave. // * // * @param session QBRTCConferenceSession instance // * @param userID publisher that left user ID // * // * @see QBRTCConferenceSession - (void)session:(QBRTCConferenceSession *)session publisherDidLeaveWithUserID:(NSNumber *)userID { } // * Called when session did receive error from server. // * // * @param session QBRTCConferenceSession instance // * @param error received error from server // * // * @note Error doesn't necessarily means that session // is closed. Can be just a minor error that can be fixed/ignored. // * // * @see QBRTCConferenceSession - (void)session:(QBRTCConferenceSession *)session didReceiveError:(NSError *)error { } // * Called when slowlink was received. // * // * @param session QBRTCConferenceSession instance // * @param uplink whether the issue is uplink or not // * @param nacks number of nacks // * // * @discussion this callback is triggered when serber // reports trouble either sending or receiving media on the // * specified connection, typically as a consequence of // too many NACKs received from/sent to the user in the last // * second: for instance, a slowLink with uplink=true // means you notified several missing packets from server, // * while uplink=false means server is not receiving // all your packets. // * // * @note useful to figure out when there are problems on // the media path (for example,, excessive loss), in order to // * possibly react accordingly (for example,, decrease the bitrate if // most of our packets are getting lost). // * // * @see QBRTCConferenceSession - (void)session:(QBRTCConferenceSession *)session didReceiveSlowlinkWithUplink:(BOOL)uplink nacks:(NSNumber *)nacks { } // * Called when media receiving state was changed on server. // * // * @param session QBRTCConferenceSession instance // * @param mediaType media type // * @param receiving whether media is receiving by server // * // * @see QBRTCConferenceSession, QBRTCConferenceMediaType - (void)session:(QBRTCConferenceSession *)session didChangeMediaStateWithType:(QBRTCConferenceMediaType)mediaType receiving:(BOOL)receiving { } // * Session did initiate close request. // * // * @param session QBRTCConferenceSession instance // * // * @discussion 'sessionDidClose:withTimeout:' will be // called after server will close session with callback // * // * @see QBRTCConferenceSession - (void)sessionWillClose:(QBRTCConferenceSession *)session { } // * Called when session was closed completely on server. // * // * @param session QBRTCConferenceSession instance // * @param timeout whether session was closed due to timeout on server // * // * @see QBRTCConferenceSession - (void)sessionDidClose:(QBRTCConferenceSession *)session withTimeout:(BOOL)timeout { } ``` </Tab> </Tabs> **Conference client interface** `QBRTCConferenceClient` is a singleton-based class that is used to create and operate with conference sessions. It has an observer (delegates) manager which can be activated/deactivated with two simple methods: <Tabs> <Tab title="Swift"> ```Swift theme={null} // * Add delegate to the observers list. // * // * @param delegate delegate that conforms to // QBRTCConferenceClientDelegate protocol // * // * @see QBRTCConferenceClientDelegate QBRTCConferenceClient.instance().add(self) // * Remove delegate from the observers list. // * // * @param delegate delegate that conforms to // QBRTCConferenceClientDelegate protocol // * // * @see QBRTCConferenceClientDelegate QBRTCConferenceClient.instance().remove(self) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // * Add delegate to the observers list. // * // * @param delegate delegate that conforms to // QBRTCConferenceClientDelegate protocol // * // * @see QBRTCConferenceClientDelegate [[QBRTCConferenceClient instance] addDelegate:self]; // * Remove delegate from the observers list. // * // * @param delegate delegate that conforms to // QBRTCConferenceClientDelegate protocol // * // * @see QBRTCConferenceClientDelegate [[QBRTCConferenceClient instance] removeDelegate:self]; ``` </Tab> </Tabs> The delegate should conform to the `QBRTCConferenceClientDelegate` protocol, which is inherited from the base client delegate. In order to create a new conference session, use the method below: <Tabs> <Tab title="Swift"> ```Swift theme={null} // * Send create session request. // * // * @note Returns session without ID. When session will be created on server // * ID will be assigned and session will be returned in 'didCreateNewSession()' callback. // * // * @see QBRTCConferenceClientDelegate // * // * @param chatDialogID chat dialog ID let session = QBRTCConferenceClient.instance().createSession(withChatDialogID: chatDialogID, conferenceType: QBRTCConferenceType.video) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // * Send create session request. // * // * @note Returns session without ID. When session will be created on server // * ID will be assigned and session will be returned in 'didCreateNewSession:' callback. // * // * @see QBRTCConferenceClientDelegate // * // * @param chatDialogID chat dialog ID // creating session QBRTCConferenceSession *session = [[QBRTCConferenceClient instance] createSessionWithChatDialogID:self.chatDialog.ID conferenceType: QBRTCConferenceTypeVideo]; ``` </Tab> </Tabs> This method will create the session locally first, without a session ID, until the server will perform a `didCreateNewSession()` callback in `QBRTCConferenceClientDelegate` protocol, where the session ID will be assigned and the session will receive its `QBRTCSessionStateNew` state. After that, you can [join](#join-video-room) or [leave](#section-leave-video-room) it. The conference session is explained below. ## Conference session `QBRTCConferenceSession` is inherited from the base session class and has all of its basics such as `state`, `currentUserID`, `localMediaStream` as well as the ability to get remote audio and video tracks for specific user IDs. <Tabs> <Tab title="Swift"> ```Swift theme={null} // * Remote audio track with opponent user ID. // * // * @param userID opponent user ID // * // * @return QBRTCAudioTrack audio track instance let audioTrack = session.remoteAudioTrack(withUserID: userID) // * Remote video track with opponent user ID. // * // * @param userID opponent user ID // * // * @return QBRTCVideoTrack video track instance let remoteVideoTraсk = session.remoteVideoTrack(withUserID: NSNumber(value: userID)) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // * Remote audio track with opponent user ID. // * // * @param userID opponent user ID // * // * @return QBRTCAudioTrack audio track instance QBRTCAudioTrack *audioTrack = [self.session remoteAudioTrackWithUserID:@(user.ID)]; // * Remote video track with opponent user ID. // * // * @param userID opponent user ID // * // * @return QBRTCVideoTrack video track instance QBRTCVideoTrack *remoteVideoTraсk = [self.session remoteVideoTrackWithUserID:opponentID]; ``` </Tab> </Tabs> It also has the ability to get a connection state for a specific user ID if their connection is opened. <Tabs> <Tab title="Swift"> ```Swift theme={null} // * Connection state for opponent user ID. // * // * @param userID opponent user ID // * // * @return QBRTCConnectionState connection state for opponent user ID let connectionState = session.connectionState(forUser: userID) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // * Connection state for opponent user ID. // * // * @param userID opponent user ID // * // * @return QBRTCConnectionState connection state for opponent user ID QBRTCConnectionState connectionState = [self.session connectionStateForUser:userID]; ``` </Tab> </Tabs> See a `QBRTCBaseSession` class for more inline documentation. As for conference-specific methods, the conference session ID is `NSNumber`. Each conference session is tied to a specific QuickBlox dialog ID (`NSString`). ## Join video room A `QBRTCConferenceSession` also has a publishers list property. But the publishers list will be only valid if you call `joinAsPublisher()` method allowing to join the session as a publisher using method below. <Tabs> <Tab title="Swift"> ```Swift theme={null} // * Perform join room as publisher. // * // * @discussion 'session:didJoinChatDialogWithID:publishersList:' will be called upon successful join. // * // * @see QBRTCConferenceClientDelegate session.joinAsPublisher() ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // * Perform join room as publisher. // * // * @discussion 'session:didJoinChatDialogWithID:publishersList:' will be called upon successful join. // * // * @see QBRTCConferenceClientDelegate [self.session joinAsPublisher]; ``` </Tab> </Tabs> This method joins the session and will publish your feed making you an active publisher in the room. Everyone in the room will be able to subscribe and receive your feed. <Note> `joinAsPublisher()` can be used **only** when the session has a valid session ID, for example, is created on the server and notified to you with `didCreateNewSession()` callback from `QBRTCConferenceClientDelegate` protocol. </Note> ## Subscribe/Unsubscribe You can subscribe and unsubscribe from publishers using the methods below. <Note> You do not need to be joined as a publisher in order to perform subscription-based operations in the session. </Note> <Tabs> <Tab title="Swift"> ```Swift theme={null} // * Subscribe to publisher's with user ID feed. // * // * @param userID active publisher's user ID // * // * @discussion If you want to receive publishers feeds, you need to subscribe to them. // * // * @note User must be an active publisher. session.subscribeToUser(withID: userID) // * Unsubscribe from publisher's with user ID feed. // * // * @param userID active publisher's user ID // * // * @discussion Do not need to be used when publisher did leave room, in that case unsubscribing will be performing automatically. Use if you need to unsubscribe from active publisher's feed. // * // * @note User must be an active publisher. session.unsubscribeFromUser(withID: userID) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // * Subscribe to publisher's with user ID feed. // * // * @param userID active publisher's user ID // * // * @discussion If you want to receive publishers feeds, you need to subscribe to them. // * // * @note User must be an active publisher. [self.session subscribeToUserWithID:userID]; // * Unsubscribe from publisher's with user ID feed. // * // * @param userID active publisher's user ID // * // * @discussion Do not need to be used when publisher did leave room, in that case unsubscribing will be performing automatically. Use if you need to unsubscribe from active publisher's feed. // * // * @note User must be an active publisher. [self.session unsubscribeFromUserWithID:userID]; ``` </Tab> </Tabs> <Note> These methods can also be used **only** when the session has a valid session ID, for example, is created on the server and is notified to you with `didCreateNewSession()` callback from `QBRTCConferenceClientDelegate` protocol. </Note> ## Mute local audio Mute the audio by calling the `localMediaStream.audioTrack.enabled()` method. Using this method, we can tell SDK to send/not send audio data from a local peer in the specified WebRTC session. <Tabs> <Tab title="Swift"> ```Swift theme={null} self.session?.localMediaStream.audioTrack.enabled = !self.session?.localMediaStream.audioTrack.enabled ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} self.session.localMediaStream.audioTrack.enabled ^= 1; ``` </Tab> </Tabs> ## Mute remote audio You can always get remote audio tracks for a specific user ID in the call using the above-specified `QBRTCSession` methods (assuming that they are existent). You can also mute remote media tracks on your side by changing the value of **enabled** property for a specific **remote media track**. <Tabs> <Tab title="Swift"> ```Swift theme={null} let remoteAudioTrack = self.session?.remoteAudioTrack(withUserID: 24450) // audio track for user 24450 //mute remoteAudioTrack.enabled = false ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCAudioTrack *remoteAudioTrack = [self.session remoteAudioTrackWithUserID:@(24450)]; // audio track for user 24450 //mute remoteAudioTrack.enabled = NO; ``` </Tab> </Tabs> ## Disable local video Turn off the video by calling `localVideoTrack.setEnabled()`. Using this method, we can tell SDK not to send video data from a local peer in the specified session. <Tabs> <Tab title="Swift"> ```Swift theme={null} self.session?.localMediaStream.videoTrack.enabled = !self.session?.localMediaStream.videoTrack.enabled ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} self.session.localMediaStream.videoTrack.enabled ^=1; ``` </Tab> </Tabs> ## Disable remote video Turn off the video by calling `localVideoTrack.setEnabled()`. Using this method, we can tell SDK not to send video data from a remote peer in the specified session. <Tabs> <Tab title="Swift"> ```Swift theme={null} let remoteVideoTraсk = session.remoteVideoTrack(withUserID: NSNumber(value: userID)) //mute remoteVideoTraсk.enabled = false ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCVideoTrack *remoteVideoTraсk = [self.session remoteVideoTrackWithUserID:opponentID]; //mute remoteVideoTraсk.enabled = NO; ``` </Tab> </Tabs> ## Leave video room To leave the session, you can perform the `leave()` method. <Tabs> <Tab title="Swift"> ```Swift theme={null} // * Leave chat room and close session. // * // * @discussion 'sessionWillClose:' will be called when all connection are closed, 'sessionDidClose:withTimeout:' will be called when session will be successfully closed by server. session.leave() ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} // * Leave chat room and close session. // * // * @discussion 'sessionWillClose:' will be called when all connection are closed, 'sessionDidClose:withTimeout:' will be called when session will be successfully closed by server. [self.session leave]; ``` </Tab> </Tabs> <Note> This method can be called in any state of the session and will always close it no matter what. </Note> ## Camera resolution It's possible to set custom video resolution using `QBRTCVideoFormat`. <Tabs> <Tab title="Swift"> ```Swift theme={null} let customVideoFormat: QBRTCVideoFormat = QBRTCVideoFormat.init(width: 950, height: 540, frameRate: 30, pixelFormat: .format420f) let cameraCapture = QBRTCCameraCapture(videoFormat: customVideoFormat, position: cameraPosition) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} QBRTCVideoFormat *customVideoFormat = [QBRTCVideoFormat videoFormatWithWidth:950 height:540 frameRate:30 pixelFormat:QBRTCPixelFormat420f]; QBRTCCameraCapture *cameraCapture = [[QBRTCCameraCapture alloc] initWithVideoFormat:customVideoFormat position:cameraPosition]; ``` </Tab> </Tabs> | Parameters | Description | | ----------- | ------------------------------------------------------ | | width | Video width. Default: **640**. | | height | Video hight. Default: **480**. | | frameRate | Video frames per second. Default: **30**. | | pixelFormat | Video pixel format. Default: **QBRTCPixelFormat420f**. | You can also get a list of available resolution formats using `formats(with:)` method and set a needed one from the list. <Tabs> <Tab title="Swift"> ```Swift theme={null} var formats = QBRTCCameraCapture.formats(with: cameraPosition) ``` </Tab> <Tab title="Objective-C"> ```Objective-C theme={null} NSArray<QBRTCVideoFormat *> *videoFormats = [QBRTCCameraCapture formatsWithPosition:cameraPosition]; // Array of possible QBRTCVideoFormat video formats for requested device ``` </Tab> </Tabs> # Address Book Source: https://docs.quickblox.com/sdks/js-address-book Learn how to store and sync the phone contact list with QuickBlox. Address Book API provides an interface to work with the phone address book. Upload it to the server and retrieve already registered QuickBlox users from your address book. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. ## Upload address book First of all, you need to upload your address book to the backend. It's a normal practice to do a full upload for the first time and then upload new contacts on future app logins. ```JavaScript JavaScript theme={null} let contact = {}; contact.name = "Apple service"; contact.phone = "1-800-275-2273"; let contactsArray = [contact]; let options = { force: 1, }; function addressBookSaved(error, result) { if (error) { console.log(error); } else { // process response } } QB.addressbook.uploadAddressBook(contactsArray, options, addressBookSaved); ``` * You also can edit an existing contact by providing a new name for it. * You also can upload more contacts, not just all in one request. They will be added to your address book on the backend. If you want to override the whole address book on the backend just provide `force: 1` option. * A device `UDID` is a unique device identifier. The `UDID` is used in cases where a user has two or more devices and contacts sync is off. Otherwise, a user has a single global address book. The `UDID` maximum length is 64 symbols. ## Retrieve address book You can retrieve your uploaded address book using the code snippet below. ```JavaScript JavaScript theme={null} function addressBookSaved(error, result) { if (error) { console.log(error); } else { // process response } } QB.addressbook.get(addressBookSaved); //with UDID QB.addressbook.get(UDID, addressBookSaved); ``` ## Update contacts If you need to update the name or phone number in your address book contacts, you should use the code sample below. ```JavaScript JavaScript theme={null} let contact = {}; contact.name = "Apple service"; contact.phone = "1-800-275-2273"; let contactsArray = [contact]; function prepareForUpdate(contacts) { return contacts.map(function (contact, index) { contact.name += " Updated"; return contact; }); } var contactsUpdated = prepareForUpdate(contactsArray); function addressBookUpdated(error, result) { if (error) { console.log(error); } else { // process response } } QB.addressbook.uploadAddressBook(contactsUpdated, addressBookUpdated); ``` ## Delete contacts You can delete contacts by using the code snippet below. ```JavaScript JavaScript theme={null} let contact = {}; contact.name = "Apple service"; contact.phone = "1-800-275-2273"; let contactsArray = [contact]; function prepareForDestroy(contacts) { return contacts.map(function (contact, index) { if (index % 2 !== 0) { delete contact.name; } contact.destroy = 1; return contact; }); } var contactsDestroy = prepareForDestroy(contactsArray); function removedContacts(error, result) { if (error) { console.log(error); } else { // process response } } QB.addressbook.uploadAddressBook(contactsDestroy, removedContacts); //with UDID var options = { udid: UDID, }; QB.addressbook.uploadAddressBook(contactsDestroy, options, removedContacts); ``` ## Retrieve registered users Using this request, you can easily retrieve the QuickBlox users - your address book contacts that are already registered in your app. Users are matched with address book contacts by phone number, so user and address book contact must have the same phone number to be included in response. ```JavaScript JavaScript theme={null} function addressBookGot(error, result) { if (error) { console.log(error); } else { // process response } } QB.addressbook.getRegisteredUsers(addressBookGot); ``` ## Push notification on new contact joined There is a way to get a push notification when some contact from your Address Book has been registered in the app. You can enable this feature at QuickBlox Dashboard. Just follow **Dashboard => *YOUR\_APP* => Users => Settings** direction and enable push notifications for new contacts. <Frame> <img alt="js-users-settings.png" /> </Frame> # Authentication Source: https://docs.quickblox.com/sdks/js-authentication Learn how to authenticate your users with QuickBlox. Every user needs to authenticate with QuickBlox before using any QuickBlox functionality. When someone connects with an application using QuickBlox, the application needs to obtain a session token which provides temporary secure access to QuickBlox APIs. A session token is an opaque string that identifies a user and an application. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. ## Session token rights There are different types of session tokens to support different use cases. | Session Token Type | Description | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Application session token | This kind of access token is needed to read the app data. Has only READ access to resources. The expiration time after the last REST API request is 2 hours. | | User session token | The user token is the most commonly used type of token. This kind of access token is needed any time the app calls an API to read, modify or write a specific user's data on their behalf. Has READ/WRITE access to resources. The expiration time after the last REST API request is 2 hours. | ## Get session At any time you can get details about your current session. ```JavaScript JavaScript theme={null} QB.getSession(function (error, session) { }) ``` ## Create session To create an application session, use the following code. ```JavaScript JavaScript theme={null} QB.createSession(function(error, result) { // callback function }); ``` To create a user session, use the following code. ```JavaScript JavaScript theme={null} var params = { login: "garry", password: "garry5santos" }; // or through email // var params = {email: '[[email protected]](/cdn-cgi/l/email-protection)', password: 'garry5santos'}; // or through social networks (Facebook) // var params = {provider: 'facebook', keys: {token: 'AM46dxjhisdffgry26282352fdusdfusdfgsdf'}}; QB.createSession(params, function(error, result) { // callback function }); ``` ## Sign up user Before you log in the user, you must create the user on QuickBlox. Recommendations are below: 1. **For POCs/MVPs:** Create the user using the [QuickBlox Dashboard](https://admin.quickblox.com/) or in client app with application session token. 2. **For production apps:** Use the [QuickBlox Create User API](https://docs.quickblox.com/reference/create-user) with [API key](/docs/application#create-api-key) on your backend to create the user when your user signs up in your app. <Warning> **Security** It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. </Warning> You can create a user with application session token in client app by calling `create()` method. ```JavaScript JavaScript theme={null} var params = { login: login, password: "someSecret", full_name: "QuickBlox Test" }; QB.users.create(params, function(error, result) { if (error) { done.fail("Create user error: " + JSON.stringify(error)); } else { } }); ``` ## Log in user QuickBlox provides four types of user authentication: login/email and password, social, phone number, and custom identity provider login. ### Login/email and password Standard login lets you log in a user just by login (or email) and password. Other fields are optional. Thus, the QuickBlox server requests a users database for a match. If there is a match, a user session is created. ```JavaScript JavaScript theme={null} var params = { login: "garry", password: "garry5santos" }; // or through email // var params = {email: '[[email protected]](/cdn-cgi/l/email-protection)', password: 'garry5santos'}; QB.login(params, function(error, result) { // callback function }); ``` ### Social Authenticate with QuickBlox using a social network access token. ```JavaScript JavaScript theme={null} var provider = "facebook"; var accessToken = "EAAWGrT9ljYYBAEZBuj1lGwmbLASHHBRmpM18pABBzdl29h5EQN0ZAfpKMZA5sCZCsb3c2tI78HpWvvP8eeZBJsflL8QWDZCrWVS5MIIAG7WgKp3I8OtZAukUzZBd92tZARFVcbuSb6yyyVobchhwfrZB4mC4ZARClvfNZCKdGbxPmO3VAsfquxK3ZAndgVQTJ8nqbro2ObA3ZCqVPUiAZDZD"; var accessTokenSecret = null; QB.createSession({ provider:provider, keys:{ token:accessToken, secret:accessTokenSecret } },function(error, result) { if (error) { } else { } }); ``` | Parameters | Description | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | provider | Authentication provider. | | keys | Access keys:- token. The access token received from the social network after a user authenticates with it.- secret. A social network provider's access token secret. | ### Phone number A sign-in with a phone number is supported with **Firebase integration**. In order to implement authentication via phone number functionality, follow this [Firebase document](https://firebase.google.com/docs/auth/web/phone-auth). Don't forget to enable phone number sign-in for your Firebase project. To learn how to do this, see this [Firebase document](https://firebase.google.com/docs/auth/web/phone-auth#enable-phone-number-sign-in-for-your-firebase-project). To send a verification code to the user's phone and sign in the user on Firebase with the received verification code, use the snippet below. ```JavaScript JavaScript theme={null} // @param {string} PhoneNumber FirebaseHelper.prototype.signInWithPhoneNumber = function (PhoneNumber) { firebase.auth().languageCode = "en"; window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier( "firebase__recaptcha_container", { size: "normal", callback: function () { firebase .auth() .signInWithPhoneNumber(PhoneNumber, window.recaptchaVerifier) .then(function (confirmationResult) { document.querySelector(".get_code").style.display = "none"; document.querySelector(".login").style.display = "block"; // SMS sent. Prompt user to type the code from the message, then sign the user with confirmationResult.confirm(code). window.confirmationResult = confirmationResult; }) .catch(function (error) { console.log("Error:", error); if (error.message) alert(error.message); document.querySelector("#firebase__recaptcha_container").innerHTML = ""; document .querySelector("#phoneNumber") .nextElementSibling.classList.add("filled"); }); }, } ); window.recaptchaVerifier.render(); }; var firebaseHelper = new FirebaseHelper(); firebaseHelper.signInWithPhoneNumber(phoneNumber); ``` To log in the user to QuickBlox, use the `login()` method and pass `authParams` to it. The ID token of the Firebase user is received as a result of the `getIdToken()` method. ```JavaScript JavaScript theme={null} var code = "confirmation code"; window.confirmationResult.confirm(code).then(function (result) { result.user.getIdToken(true).then(function (idToken) { var authParams = { provider: "firebase_phone", firebase_phone: { access_token: idToken, project_id: "Your projectId", }, }; QB.login(authParams, function (error, user) { if (error) { // check the error } else { // user - logged-in user (response from https://docs.quickblox.comhttps://docs.quickblox.com/reference/authentication#log-in) } }); }); }).catch(function (error) { console.error(error); }); ``` Pass the following arguments to the `login()` method. | Arguments | Required | Description | | ---------- | -------- | ---------------------------------------------------------------- | | authParams | yes | Specifies Firebase authentication parameters that should be set. | The `authParams` object includes the following fields. | Fields | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | provider | Authentication provider. Possible values: facebook, firebase\_phone. | | firebase\_phone | Specifies the firebase\_phone object fields that should be set:- project\_id. Firebase project ID. When you create a Firebase project, Firebase automatically assigns a unique ID to the project, but you can edit it during the project setup.- access\_token. ID token of the Firebase user. Created by Firebase when a user signs in to an app. This token is received as a result of getIdToken() method. | ### Custom identity provider You can authenticate your application users from the external database with QuickBlox via Custom Identity Provider (CIdP). Just specify the user ID and access token as a password to authenticate with QuickBlox. Review [Custom Identity Provider](/docs/custom-identity-provider) page for more details on the feature. ```JavaScript JavaScript theme={null} var params = { login: "4324", password: "8b75a6c7191285499d890a81df4ee7fe49bc732a" }; QB.login(params, function(error, result) { // callback function }); ``` <Warning> This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). </Warning> ## Log out user If you have a user session, you can downgrade it to an application session by calling `logout()` method. ```JavaScript JavaScript theme={null} QB.logout(function(error) { // callback function }); ``` ## Session expiration The expiration time for a session token is 2 hours. If you will perform a query with an expired token, you will receive an error: `Required session does not exist`. In this case, you have to recreate the session token. In JS SDK since version 2.14.1 we have a listener to find if the session token has expired. ```JavaScript JavaScript theme={null} QB.chat.onSessionExpiredListener = function(error){ if (error) { console.log('onSessionExpiredListener - error: ', error); } else{ console.log('The session has expired.'); } } ``` If you use JS SDK version early then 2.14.1 in order to found out the session has expired you should use code below to initialize JS SDK. ```JavaScript JavaScript theme={null} var APPLICATION_ID = 41; var AUTH_KEY = "lkjdueksu7392kj"; var AUTH_SECRET = "iiohfdija792hj"; var ACCOUNT_KEY = "sdjfnksnlk2bk1k34kb"; var CONFIG = { debug: true, on: { sessionExpired: async function(handleResponse, retry) { console.log('The session has expired.'); }, }, }; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY, CONFIG); ``` ## Destroy session token To destroy a session, use the following code. ```JavaScript JavaScript theme={null} QB.destroySession(function(error) { // callback function }); ``` ## Set existing session Typically, a session token is stored in SDK after successful login and used for every subsequent API call. However, you may want to obtain and store the session on your server for better security. In this case, since the version JS SDK 2.14.1, you can set the existing session token into SDK using the `startSessionWithToken(sessionToken, callBack)` method. It's can be [application or user token](https://docs.quickblox.com/reference/authentication#application-vs-user-session-token). Don't forget to [Init](/sdks/js-setup#install-quickblox-sdk-into-your-app) and [log in user](/sdks/js-authentication#log-in-user) if you pass the application token. ```JavaScript JavaScript theme={null} QB.startSessionWithToken(sessionToken, function(err, mySession){ if (err){ console.log('Error in start session with token'); } else { console.log('session data: ', mySession); } }); ``` # Connection Source: https://docs.quickblox.com/sdks/js-chat-connection Learn how to connect to the chat server and set connection settings. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Connect to Chat server To connect to Chat server use the code snippet below. ```JavaScript JavaScript theme={null} var userCredentials = { userId: 4448514, password: "awesomepwd" }; QB.chat.connect(userCredentials, function(error, contactList) {}); ``` ## Connect to chat server with QuickBlox session token In case, you authenticate with QuickBlox via Firebase, Facebook, or Custom Identity Provider, you should connect to the chat server with QuickBlox user session token and QuickBlox user ID. As a result of successful authentication via Firebase, Facebook, or Custom Identity Provider, you receive a QuickBlox user session token and QuickBlox user ID. Use QuickBlox user session token as a password and QuickBlox user ID as a login to connect to the chat server. <Warning> Don't use the Firebase/Facebook access token as a password to connect to the chat server. You will receive the following runtime error in this case: `<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><not-authorized/><text xml:lang='en'>Password not verified</text></failure>`. </Warning> ```JavaScript JavaScript theme={null} var session = QB.service.getSession(); var userId = session.user_id; var password = session.token; var params = {userId, password}; QB.chat.connect(params, function(error, contactList) {}); ``` The `connect()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | -------- | -------- | ------------------------------------------------------------------- | | userId | yes | ID of the user. | | password | yes | Specifies an active QuickBlox user session token set as a password. | ## Disconnect from Chat server Disconnect from the Chat server using the snippet below. ```JavaScript JavaScript theme={null} QB.chat.disconnect(); QB.chat.onDisconnectedListener = onDisconnectedListener; function onDisconnectedListener() { console.log("onDisconnected"); } ``` ## Reconnection QuickBlox Chat runs over XMPP protocol. To receive messages in a real-time mode, the application should be connected to the Chat over XMPP protocol. The SDK can be reconnected to the Chat server when the connection is lost. The following 2 callbacks are used to track the state of the connection. ```JavaScript JavaScript theme={null} QB.chat.onDisconnectedListener = onDisconnectedListener; QB.chat.onReconnectListener = onReconnectListener; function onDisconnectedListener() {} function onReconnectListener() {} ``` # Contact List Source: https://docs.quickblox.com/sdks/js-chat-contact-list Learn how to create, update, manage, and add users to your contact list. The Contact List API is rather straightforward. User A sends a request to become "friends" with user B. User B accepts the friend request. And now user A and B appear in each other's roster. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/js-chat-connection) page to learn how to do it. ## Access contact list You can access the contact list on your login to chat. The contact list object will be returned in callback. Also, the following function gives you an access to contact list. ```JavaScript JavaScript theme={null} try { QB.chat.roster.get(function(contactlist) { }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` ## Add user to your contact list To add a user to the contact list, use the `add()` method. ```JavaScript JavaScript theme={null} var userId = 34; try { QB.chat.roster.add(userId, function() { }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` <Note> Maximum number of contacts is 300. </Note> Another user will receive a request to be added to the contact list. The `onSubscribeListener` callback will be called. ```JavaScript JavaScript theme={null} QB.chat.onSubscribeListener = function(userId) { }; ``` ## Confirm the contact request To confirm the request, use the `confirm()` method. ```JavaScript JavaScript theme={null} try { QB.chat.roster.confirm(userId, function() { }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` A user will be informed that you have accepted the contact request by the `onConfirmSubscribeListener` callback. ```JavaScript JavaScript theme={null} QB.chat.onConfirmSubscribeListener = function(userId) { }; ``` ## Reject the contact request To reject the request, use the `reject()` method. ```JavaScript JavaScript theme={null} try { QB.chat.roster.reject(userId, function() { }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` A user will be informed that you have declined the contact request by `onRejectSubscribeListener` callback. ```JavaScript JavaScript theme={null} QB.chat.onRejectSubscribeListener = function(userId) { }; ``` ## Remove user from the contact list To remove a previously added user from the contact list, use the following method. ```JavaScript JavaScript theme={null} try { QB.chat.roster.remove(userId, function() { }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` ## Contact list updates You can also track contact list updates in real-time by using the code snippet below. ```JavaScript JavaScript theme={null} QB.chat.onContactListListener = function(userId, type) { // type - if a user left the chat, type will be 'unavailable'. // Otherwise - 'available'. }; ``` # Dialogs Source: https://docs.quickblox.com/sdks/js-chat-dialogs Learn how to create and manage dialogs. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/js-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Dialog types All chats between users are organized in dialogs. There are 3 types of dialogs: * **private dialog** - a dialog between 2 users. * **group dialog** - a dialog between the specified list of users. * **public dialog** - an open dialog. Any user from your app can be joined to it. You need to create a new dialog and then use it to chat with other users. You also can obtain a list of your existing dialogs. ## Create dialog To create a **private** dialog, you need to set the dialog `type` to `3` and ID of an opponent you want to create a chat with. ```JavaScript JavaScript theme={null} var params = { type: 3, occupants_ids: [56] }; QB.chat.dialog.create(params, function(error, dialog) {}); ``` To create a **group** dialog for a predefined number of occupants, you need to set the dialog `type` to `2` and IDs of opponents, you want to create a chat with. ```JavaScript JavaScript theme={null} var params = { type: 2, occupants_ids: [56, 98, 34], name: "Hawaii relax team" // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // photo: "" }; QB.chat.dialog.create(params, function(error, dialog) {}); ``` It's possible to create a **public** dialog, so any user from your application can be joined to it. There is no list of occupants. This dialog is open for everybody. You just need to set the dialog `type` to `1` and a name for a new dialog. ```JavaScript JavaScript theme={null} var params = { type: 1, name: "Blockchain trends" // Photo can be a link to a file in Content module, Custom Objects module or just a web link. // photo: "" }; QB.chat.dialog.create(params, function(error, dialog) {}); ``` ## Create dialog with custom parameters A dialog can be extended with additional parameters. These parameters can be used to store additional data. Also, these parameters can be used in dialogs retrieval requests. To start using additional parameters, create an additional schema of your parameters. This is a custom objects class. Just create an empty class with all fields that you need. These fields will be additional parameters in your dialog. See [this section](/sdks/js-custom-objects) to learn how to create a schema using Custom Objects. Then, specify the parameters defined in the schema in a new dialog. ```JavaScript JavaScript theme={null} // you should already have created a 'CoolDialog'custom objects class var params = { type: 2, name: "My friends", data:{ "class_name":"CoolDialog", "category":"friends" } }; QB.chat.dialog.create(params, function(error, dialog) {}); ``` Set the following fields of the `params`. | Field | Required | Description | | ----- | -------- | ------------------------------------------------------------------------------------------------------------------- | | type | yes | Dialog type. There tree dialog types:- type: 1 - public dialog.- type: 2 - group dialog.- type: 3 - private dialog. | | name | yes | Dialog name. | | data | yes | Specifies additional parameters in a new dialog. | ## Create group dialog with join required <Warning> Available since **QuickBlox JavaScript SDK v2.19.0**. If you use TypeScript, type definitions for `is_join_required` (including the `QBDialogCreateParams` type and runtime validation) are available starting from **v2.23.0**. Prior to **server version 2.34.0**, all group dialogs required joining. Starting from **server version 2.34.0**, new applications do not require joining, while existing applications retain the previous behavior. You can change the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). If `is_join_required` is explicitly set when creating a dialog, the provided value takes priority over the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). Most applications do not need this feature. The default behavior where participants can send and receive real-time messages without joining is recommended for most use cases. </Warning> When creating a **group** dialog, you can set the `is_join_required` parameter to `1` to require participants to explicitly join the dialog before they can send or receive real-time messages. This is only needed when you want to restrict real-time messaging in specific dialogs until participants explicitly join. By default, `is_join_required` is `0` and participants can message without joining. This parameter applies **only to group dialogs** (type `2`). You can change the default in [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). ```JavaScript JavaScript theme={null} var params = { type: 2, occupants_ids: [56, 98, 34], name: "Hawaii relax team", is_join_required: 1 }; QB.chat.dialog.create(params, function(error, dialog) { if (error) { console.error(error); } else { var isJoinRequired = dialog.is_join_required; console.log("is_join_required:", isJoinRequired); } }); ``` The value of `is_join_required` must be `0` or `1`. Invalid values (e.g., `2`, `'yes'`, `-1`) are silently ignored and the dialog is created without this parameter, using the default from [application settings](/docs/application#set-default-join-required-for-new-group-dialogs). <Note> Changing `is_join_required` via `QB.chat.dialog.update()` is **not supported** in the SDK. The parameter can only be set when creating a dialog. To change `is_join_required` for an existing dialog, use the [Server API](/reference/update-dialog) directly. </Note> ## Check if join required for group dialog <Info> The `is_join_required` field is available starting from **QuickBlox JavaScript SDK v2.19.0**. See [Create group dialog with join required](/sdks/js-chat-dialogs#create-group-dialog-with-join-required) for details. </Info> You can get the `is_join_required` value for any group dialog: ```JavaScript JavaScript theme={null} var isJoinRequired = dialog.is_join_required; ``` ## Join group dialog <Info> Starting from **QuickBlox JavaScript SDK v2.19.0**, joining a group dialog is required only when `is_join_required` is set to `1` for a dialog. See [Create group dialog with join required](/sdks/js-chat-dialogs#create-group-dialog-with-join-required) for details. </Info> If `is_join_required` is set to `1` for a group dialog, you need to join it by calling the `join()` method before you can send or receive real-time messages. See [this section](/sdks/js-chat-messaging#send-text-message) to learn how to send/receive real-time messages. You must join the dialog after every new connection or reconnection. If the connection is lost and then restored, whether manually or automatically, you need to call `join()` again for each dialog where `is_join_required` is `1`. ```JavaScript JavaScript theme={null} var dialogJid = QB.chat.helpers.getRoomJidFromDialogId(dialog._id); try { QB.chat.muc.join(dialogJid, function(error, result) { if (error) { console.error(error); } else { // successfully joined } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` | Argument | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | dialogJid | yes | Room JID. JID (Jabber ID) of XMPP room in the XMPP server. Generated automatically by the server after dialog creation. You can get JID from the dialog ID. The JID format is the following: `<app_id>-<dialog_id>@muc.chat.quickblox.com` | | function() | yes | Specifies a callback function that accepts an error and result. | <Note> You can join a group dialog **only** if your user ID is present in the `occupants_ids` array in the dialog model. Your user ID is added to the `occupants_ids` array if you create a dialog **or** you are added to the dialog by another user. See [this section](/sdks/js-chat-dialogs#add-occupants) to learn how to add occupants to the group dialog. </Note> ## Join public dialog Before you start chatting in a **public** dialog, you must join it by calling the `join()` method. Unlike group dialogs, joining a public dialog is always required. If you've successfully joined the dialog, you can send/receive real-time messages. See [this section](/sdks/js-chat-messaging#send-text-message) to learn how to send/receive real-time messages. You must join the dialog after every new connection or reconnection. If the connection is lost and then restored, whether manually or automatically, you need to call `join()` again. ```JavaScript JavaScript theme={null} var dialogJid = QB.chat.helpers.getRoomJidFromDialogId(dialog._id); try { QB.chat.muc.join(dialogJid, function(error, result) { if (error) { console.error(error); } else { // successfully joined } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` ## Leave group dialog You can leave the **group** dialog by calling the `leave()` method. After leaving, you will stop receiving real-time messages from this dialog. You need to join the dialog again to resume receiving real-time messages. <Info> Starting from **QuickBlox JavaScript SDK v2.19.0**, leaving a group dialog is only needed when `is_join_required` is set to `1`. If `is_join_required` is `0`, you do not need to call `leave()`. </Info> ```JavaScript JavaScript theme={null} var dialogJid = QB.chat.helpers.getRoomJidFromDialogId(dialog._id); try { QB.chat.muc.leave(dialogJid, function(error) { if (error) { console.error(error); } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` | Argument | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | dialogJid | yes | Room JID. JID (Jabber ID) of XMPP room in the XMPP server. Generated automatically by the server after dialog creation. You can get JID from the dialog ID. The JID format is the following: `<app_id>-<dialog_id>@muc.chat.quickblox.com` | | function() | yes | Specifies a callback function that accepts an error. | <Note> When you leave a group dialog, your user ID is still present in the `occupants_ids` array in the dialog model. The dialog will still appear in the list of dialogs and you will still have access to the chat history. To remove yourself from the group dialog, use the `update()` method. See [this section](/sdks/js-chat-dialogs#remove-occupants) to learn how to remove occupants from the group dialog. </Note> ## Leave public dialog You can leave the **public** dialog by calling the `leave()` method. After leaving, you will stop receiving real-time messages from this dialog. You need to join the dialog again to resume receiving real-time messages. ```JavaScript JavaScript theme={null} var dialogJid = QB.chat.helpers.getRoomJidFromDialogId(dialog._id); try { QB.chat.muc.leave(dialogJid, function(error) { if (error) { console.error(error); } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` ## Retrieve online users You can get a list of online users from the dialog. Call the `listOnlineUsers()` method to get the list of online users who are joined to the dialog. As a result, an array of user IDs is returned. ```JavaScript JavaScript theme={null} var dialogId = "..."; var dialogJid = QB.chat.helpers.getRoomJidFromDialogId(dialogId); try { QB.chat.muc.listOnlineUsers(dialogJid, function(users) { }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` Let's see, how the `listOnlineUsers()` method is used with regard to the dialog type. | Capabilities | Public | Group | Private | | --------------------- | ------ | ----- | ------- | | Retrieve online users | ✗ | ✓ | ✗ | <Note> You can retrieve online users from the group dialog **only** if you are joined to it. </Note> ## Retrieve list of dialogs It's common to request all your dialogs on every app login. The request below will return all **private**, **group**, and **public** dialogs created before the current date, sorted in descending order by the `created_at` field, and limited to 10 dialogs on the page. ```JavaScript JavaScript theme={null} let params = { created_at: { lt: Date.now()/1000 }, sort_desc: 'created_at', limit: 10 }; QB.chat.dialog.list(params, function(error, dialogs) { }); ``` | Argument | Required | Description | | ---------- | -------- | ------------------------------------------------------ | | params | no | Specifies param fields that should be set. | | function() | yes | A callback function that an accepts error and dialogs. | If you want to retrieve only dialogs updated after some specific date time and order the search results, you can apply operators. This is useful if you cache dialogs somehow and do not want to obtain the whole list of your dialogs on every app start. Thus, you can apply [search](/sdks/react-native-chat-dialogs#search-operators) and [sort](/sdks/react-native-chat-dialogs#sort-operators) operators to the list of dialogs on the page so that it is easier to view specific dialogs. If you want to get a paginated list of dialogs or just a count of dialogs from the server, you can set the following fields of the `filter`: | Field | Required | Description | | ----- | -------- | ----------------------------------------------------------------------------------------------------------------- | | skip | no | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. Should be an Integer. | | limit | no | Limit search results to N records. Useful for pagination. Default value: **100**. | ### Search operators You can use search operators to get more specific search results. The request below will return a list of all **private** and **group** dialogs. ```JavaScript JavaScript theme={null} let params = { type: { in: [2,3] } }; QB.chat.dialog.list(params, function(error, dialogs) { }); ``` Here are the search operator that you can use to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | --------------------------------------------------- | ---------------------------------------------------- | | lt | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** operator. | | lte | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** or **Equal** to operator. | | gt | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** operator. | | gte | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** or **Equal** to operator. | | ne | number, string, date | \_id, name, last\_message\_date\_sent | **Not Equal** to operator. | | in | number, string, date | type, last\_message\_date\_sent, name | **IN** array operator. | | nin | number, string, date | last\_message\_date\_sent | **IN** array operator. | | all | number | occupants\_ids | **ALL** are contained in array. | | ctn | string | name | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return dialogs created since the beginning of this year and sorted in descending order. ```JavaScript JavaScript theme={null} let params = { created_at: { gte: (new Date(new Date().getFullYear(), 0, 1))/1000 }, sort_desc: 'sort_desc' }; QB.chat.dialog.list(params, function(error, dialogs) { }); ``` Here are the sort operators that you can use to order search results. | Sort operator | Applicable to types | Applicable to fields | Description | | ------------- | ------------------- | ------------------------------------------------ | ------------------------------------------------------------------------- | | sort\_asc | All types | id, created\_at, name, last\_message\_date\_sent | Search results will be sorted in ascending order by the specified field. | | sort\_desc | All types | id, created\_at, name, last\_message\_date\_sent | Search results will be sorted in descending order by the specified field. | ### Aggregation operators You can use an aggregation operator to count search results. The request below will return a count of all **group** dialogs from the server. ```JavaScript JavaScript theme={null} let params = { type: 2, count:1 }; QB.chat.dialog.list(params, function(err, result) { if (err) { console.log(err); } else { } }); ``` Here are the aggregation operators you can use to retrieve dialogs. | Aggregation operator | Description | | -------------------- | ------------------------------------------------------------------------------------------------------- | | count | Count search results. The response will contain only a count of records found. Set count to 1 to apply. | ## Retrieve dialogs by custom parameters You can retrieve dialogs by custom parameters. The request below will return all **private**, **group**, and **public** dialogs with a given `category` and `class_name`. ```JavaScript JavaScript theme={null} var filters = { data: { // search by custom parameters category:'friends', class_name:'CoolDialog' } }; QB.chat.dialog.list(filters, function(error, dialogs) {}); ``` ## Update dialog You can update the information for a **private**, **group**, and **public** dialog. ```JavaScript JavaScript theme={null} var dialogId = "5356c64ab35c12bd3b108a41"; var toUpdateParams = { name: "Tesla club" }; QB.dialog.update(dialogId, toUpdateParams, function(error, dialog) {}); ``` Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | ------------------------ | -------------- | -------------- | -------------- | | Update a dialog name | Owner | Owner | ✗ | | Update a photo | Owner | Owner | ✗ | | Update custom parameters | Owner,Occupant | Owner,Occupant | Owner,Occupant | ## Add occupants Set the `push_all` field to add occupants to the dialog. As a result, the ID of the opponent will be added to the `occupants_ids` array. ```JavaScript JavaScript theme={null} let dialogId = "5356c64ab35c12bd3b108a41", toUpdateParams = { push_all: { occupants_ids: [97, 789] } }; QB.chat.dialog.update(dialogId, toUpdateParams, function(error, dialog) {}); ``` | Arguments | Required | Description | | -------------- | -------- | ------------------------------------------------------- | | dialogId | yes | Dialog ID. | | toUpdateParams | yes | Specifies the toUpdateParams fields that should be set. | Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | --------------- | ------------- | -------------- | -------------- | | Add other users | ✗ | Owner,Occupant | ✗ | ## Remove occupants Set the `pull_all` field to remove occupants from the dialog. As a result, the ID of the opponent will be removed from the `occupants_ids` array. ```JavaScript JavaScript theme={null} let dialogId = "5356c64ab35c12bd3b108a41", toUpdateParams = { pull_all: { occupants_ids: [97, 789] } }; QB.chat.dialog.update(dialogId, toUpdateParams, function(error, dialog) {}); ``` | Argument | Required | Description | | -------------- | -------- | ------------------------------------------------------- | | dialogId | yes | Dialog ID. | | toUpdateParams | yes | Specifies the toUpdateParams fields that should be set. | Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | ------------------ | ------------- | -------------- | -------------- | | Remove other users | ✗ | Owner | ✗ | | Remove yourself | ✗ | Owner,Occupant | ✗ | ## Delete dialog The request below will remove the dialog for a current user, but other users will be still able to chat there. ```JavaScript JavaScript theme={null} var dialogId = "5356c64ab35c12bd3b108a41"; QB.chat.dialog.delete([dialogId], function(error) { }); ``` Set the `force` to `1` to completely remove the dialog for all users. You can also delete multiple dialogs in a single request. ```JavaScript JavaScript theme={null} var dialogId = "5356c64ab35c12bd3b108a41"; QB.chat.dialog.delete([dialogId], {force: 1}, function(error) { }); ``` Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public | Group | Private | | ----------------------------------------------- | ------ | -------------- | -------------- | | Delete dialog for all userusing forceparameter. | Owner | Owner | Owner | | Delete dialog for a current user | Owner | Owner,Occupant | Owner,Occupant | ## Get number of unread messages You can get a number of unread messages from a particular dialog using the `list()` method. ```JavaScript JavaScript theme={null} var filters = { limit: 1 }; QB.chat.dialog.list(filters, function(error, dialogs) { if (error) { // handle error } else { var dialog = dialogs.items[0]; var unreadCount = dialog.unread_messages_count; } }) ``` You can also retrieve **total** unread messages count using `unreadCount()` method. ```JavaScript JavaScript theme={null} var params = { chat_dialog_ids: ["5356c64ab35c12bd3b108a41"] }; QB.chat.message.unreadCount(params, function(error, result) { }); ``` Set the following fields of the `params`: | Field | Description | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | chat\_dialog\_ids | IDs of dialogs.- If chat\_dialog\_ids are **not** specified, the total number of unread messages for **all** dialogs of the user will be returned.- If chat\_dialog\_ids are specified, the number of unread messages for each specified dialog will be returned. Also, the total number of unread messages for all dialogs of the user will be returned. | ## Resources A sequence of steps a user takes to start a dialog by moving through the application lifecycle. <img alt="javascript-starting-dialog.jpg" /> # Messaging Source: https://docs.quickblox.com/sdks/js-chat-messaging Learn how to send and receive messages, mark messages as delivered or read, etc. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/js-chat-connection) page to learn how to do it. 5. Create a dialog. See [Dialogs](/sdks/js-chat-dialogs) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Subscribe message events Use the `QB.chat.onMessageListener` to receive messages in real-time. The event listener enables the app to listen to the incoming message. ```JavaScript JavaScript theme={null} function onMessage(userId, message) { }; QB.chat.onMessageListener = onMessage; ``` ## Send text message To send a message to a **private** dialog, use the code snippet below. ```JavaScript JavaScript theme={null} var dialog = "..."; var message = { type: "chat", body: "How are you today?", extension: { save_to_history: 1, dialog_id: dialog._id }, markable: 1 }; var opponentId = 78; try { message.id = QB.chat.send(opponentId, message); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } //... QB.chat.onMessageListener = onMessage; function onMessage(userId, message) {} ``` To send a message to a **group** or **public** dialog, use the code snippet below. <Note> You need to join the **group** and **public** dialog by calling the `join()` method before you start chatting in a dialog. Once the dialog is joined, you can receive/send messages. See [this section](/sdks/js-chat-dialogs#join-dialog) to learn how to join the dialog. </Note> ```JavaScript JavaScript theme={null} var message = { type: "groupchat", body: "How are you today?", extension: { save_to_history: 1, dialog_id: dialog._id }, markable: 1 }; var dialogJid = QB.chat.helpers.getRoomJidFromDialogId(dialog._id); try { message.id = QB.chat.send(dialogJid, message); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } //... QB.chat.onMessageListener = onMessage; function onMessage(userId, message) {} ``` | Argument | Required | Description | | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | dialogJid | yes | Room JID. JID (Jabber ID) of XMPP room in the XMPP server. Empty for a privatе dialog. Generated automatically by the server after dialog creation. You can get JID from the dialog ID. The JID format is the following: `<app_id>-<dialog_id>@muc.chat.quickblox.com` | | message | yes | Specifies message fields that should be set. | <Note> Make sure to set the `save_to_history: 1` to save the message on the server. If you set `save_to_history: 0`, the message won't be saved on the server. However, the message will be delivered to the user in either case. </Note> ## Send message with attachment Chat attachments are supported by [content API](/sdks/js-content). In order to send a chat attachment, you need to upload the file to QuickBlox cloud storage and obtain a link to the file (file UID). Then you need to include this UID into the chat message and send it. ```JavaScript JavaScript theme={null} // for example, a file from HTML form input field var inputFile = $("input[type=file]")[0].files[0]; var fileParams = { name: inputFile.name, file: inputFile, type: inputFile.type, size: inputFile.size, public: false }; QB.content.createAndUpload(fileParams, function(error, result) { if (!error) { // prepare a message var message = { type: dialog.type === 3 ? "chat" : "groupchat", body: "[attachment]", extension: { save_to_history: 1, dialog_id: dialog._id, attachments: [{ id: result.uid, type: "image" }] } }; // send the message // ... } }); ``` If you are running **Node.js** environment, the following code snippet can be used to access a file. ```JavaScript JavaScript theme={null} const fs = require("fs"); var imagePath = __dirname + "/dog.jpg"; fs.stat(imagePath, function (statError, stats) { if (statError) { throw statError; } fs.readFile(imagePath, function (readError, data) { if (readError) { throw readError; } var fileParams = { file: data, name: "image.jpg", type: "image/jpeg", size: stats.size, }; // upload // ... }); }); ``` The flow on the receiver's side is the following: when you receive a message, you need to get the file URL to download the file from the cloud storage. ```JavaScript JavaScript theme={null} QB.chat.onMessageListener = function(userId, message) { if (message.extension.hasOwnProperty("attachments")) { if (message.extension.attachments.length > 0) { var fileUID = message.extension.attachments[0].uid; var fileUrl = QB.content.privateUrl(fileUID); var imageHTML = "<img src='" + fileUrl + "' alt='photo'/>"; } } }; ``` ## Send message with extra data You have an option to extend the message with additional fields. Specify one or more **key-value** items in the `message`. Using these items, you can implement the ability for a user to send self-location information to another user or notification messages signifying that a user has left a group, etc. ```JavaScript JavaScript theme={null} var message = { type: "groupchat", body: "How are you today?", extension: { save_to_history: 1, customParam1: "book", customParam2: "21", }, }; try { QB.chat.send(jidOrUserId, message); } catch (e) { if (e.name === "ChatNotConnectedError") { // not connected to chat } } ``` | Argument | Required | Description | | ----------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | jidOrUserId | yes | User ID or Room JID. JID (Jabber ID) of XMPP room in the XMPP server. Empty for a privatе dialog. Generated automatically by the server after dialog creation. You can get JID from the dialog ID. The JID format is the following: `<app_id>-<dialog_id>@muc.chat.quickblox.com`. | | message | yes | Specifies message fields that should be set. | Set the following fields of the `message`: | Field | Required | Description | | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | type | no | Message type. Possible values: chat and groupchat. | | body | no | A message text. | | extension | no | Extra data. Specify any key-value pairs. In each pair, the key and value are both string values. Set save\_to\_history as true to save the message on the server. | ## Retrieve chat history Every dialog stores its chat history that you can retrieve using the `list()` method. The request below will return messages for a specific dialog, sorted by the `date_sent` field in descending order, limited to 100 messages. ```JavaScript JavaScript theme={null} var dialogId = "5356c64ab35c12bd3b108a41"; var params = { chat_dialog_id: dialogId, sort_desc: 'date_sent', limit: 100, skip: 0 }; QB.chat.message.list(params, function(error, messages) { }); ``` <Warning> If you want to mark all retrieved chat messages as a read, set the `markAsRead` parameter as `true`. If you decide not to mark chat messages as read, just set `markAsRead` parameter as `false`. </Warning> If you want to retrieve only messages updated after some specific date time and order the search results, you can apply operators. This is useful if you cache messages somehow and do not want to obtain the whole list of messages on every app start. Thus, you can apply [search](/sdks/android-chat-messaging#search-operators) and [sort](/sdks/android-chat-messaging#sort-operators) operators to list messages on the page so that it is easier to view specific messages. If you want to get a paginated list of messages from the server, you can set the following fields of the `params`: | Pagination parameter | Description | | -------------------- | ------------------------------------------------------------------------------------------- | | skip | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | Limit search results to N records. Useful for pagination. Default value: **100**. | ### Search operators You can use search operators to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | ---------------------------------------------------- | ---------------------------------------------------- | | lt | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** operator. | | lte | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** or **Equal** to operator. | | gt | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** operator. | | gte | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** or **Equal** to operator. | | ne | number, string, date | \_id, message, date\_sent, sender\_id, recipient\_id | **Not Equal** to operator. | | in | number, string, date | date\_sent, sender\_id, recipient\_id | **IN** array operator. | | nin | number, string, date | date\_sent, sender\_id, recipient\_id | Not **IN** array operator. | | or | number, string, date | date\_sent, sender\_id, recipient\_id | All records that contain a value 1 **or** value 2. | | ctn | string | message | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. | Sort operator | Applicable to types | Description | | ------------- | ------------------- | ------------------------------------------------------------------------- | | sort\_asc | All types | Search results will be sorted in ascending order by the specified field. | | sort\_desc | All types | Search results will be sorted in descending order by the specified field. | ## Update message Update the message text using the `update()` method. ```JavaScript JavaScript theme={null} var messageId = '5f3a7fd75799727266000001'; var updateParams = { chat_dialog_id: '5f3166c4a0eb47592eab07aa', message: 'Message updated!' }; QB.chat.message.update(messageId, updateParams, function (error) { // if error occurred - error will be returned }); ``` | Argument | Required | Description | | ------------ | -------- | --------------------------------------------------------------------------------------------------------------- | | messageId | yes | ID of the message being updated. | | updateParams | yes | Specifies key-value fields that should be updated. | | function() | yes | Specifies the callback function which receives the response from the QuickBlox server for the updating request. | ## Delete message Any user in the `occupantIDs` can delete a message from the dialog. As a result, the message will be deleted from the current user history, without affecting the histories of other users. The owner of the dialog can completely remove messages from all users' histories. This is achieved by setting the `force` to `1`. ```JavaScript JavaScript theme={null} var params = { force: 1 // remove message from all users' histories }; QB.chat.message.delete(messageId, params, function(error, result) { }); ``` Set the following fields of the `params`: | Argument | Required | Description | | --------- | -------- | ------------------------------------------- | | messageId | yes | ID of the message. | | params | yes | Specifies params fields that should be set. | ## Check if a message is sent The message is considered as **sent** if it has been delivered to the server. To get to know that a message has been delivered to the server, make sure to enable a stream management before connecting to the Chat server. Enable it using the `CONFIG` object and then call the `init()` method. See [this section](/sdks/js-setup#stream-management) to learn how to enable the stream management. Thus, to handle the event when the message is considered as **sent**, the `onSentMessageCallback()` is used. ```JavaScript JavaScript theme={null} QB.chat.onSentMessageCallback = function (messageLost, messageSent) { }; ``` <Warning> You should enable Stream Management before you do the `login()` because the Stream Management is initialized while Chat login is performed. The Stream Management defines an extension for active management of a stream between a client and server, including features for stanza acknowledgments. </Warning> ## Mark message as delivered As a sender, you may want to be informed that a message has been successfully delivered to the recipient. The mark-as-delivered functionality allows to notify the sender about message delivery. To track the event when the message has been delivered to the user, use the `QB.chat.onMessageListener`. See [this section](/sdks/js-chat-messaging#subscribe-message-events) to learn how to add the listener. As a result, when a user receives a message, the SDK receives the `onDeliveredStatusListener()` callback. ```JavaScript JavaScript theme={null} QB.chat.onDeliveredStatusListener = function(messageId, dialogId, userId) { } ``` Use the `sendDeliveredStatus()` method to mark a message as delivered. As a result, the server will notify a sender about the delivery receipt. ```JavaScript JavaScript theme={null} var params = { messageId: "557f1f22bcf86cd784439022", userId: 21, dialogId: "5356c64ab35c12bd3b108a41", }; try { QB.chat.sendDeliveredStatus(params); } catch (e) { if (e.name === "ChatNotConnectedError") { // not connected to chat } } ``` A message can be marked as delivered automatically by the server once a message is successfully delivered to the recipient. Set the `markable` parameter and pass it within the `message` to the `send()` method if you want, as a sender, to receive message delivery receipts from other recipients. Thus, the `markable` parameter enables the sender to request the delivery receipt. It also enables the recipient to confirm the message delivery. However, if `markable` is not set or omitted, then you can notify a sender about the delivery receipt using the `sendDeliveredStatus()` method. ```JavaScript JavaScript theme={null} var message = { type: "chat", body: "How are you today?", extension: { save_to_history: 1, dialog_id: dialog._id, }, markable: 1, }; var opponentId = 78; try { message.id = QB.chat.send(opponentId, message); } catch (e) { if (e.name === "ChatNotConnectedError") { // not connected to chat } } ``` <Note> Make sure to understand, that marking-as-delivered operation just confirms the fact of message delivery. The message acquires the **delivered** status when the `onDeliveredStatusListener` callback is received. When a message is marked as delivered, the IDs of users who have received the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who received the message using the `list()` method. See [this section](/sdks/js-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Note> ## Mark message as read As a sender, you may want to be informed that a message has been read by the recipient. The mark-as-read functionality allows to notify the sender that a message has been read. To track the event when the message has been read by the user, use the `QB.chat.onMessageListener`. See [this section](/sdks/js-chat-messaging#subscribe-message-events) to learn how to add the listener. As a result, when a user receives a message, the SDK receives the `onReadStatusListener()` callback. ```JavaScript JavaScript theme={null} QB.chat.onReadStatusListener = function(messageId, dialogId, userId) { }; ``` Use the `sendReadStatus()` method to mark a message as read. As a result, the server will notify a sender about the read receipt. ```JavaScript JavaScript theme={null} var params = { messageId: "557f1f22bcf86cd784439022", userId: 21, dialogId: "5356c64ab35c12bd3b108a41", }; try { QB.chat.sendReadStatus(params); } catch (e) { if (e.name === "ChatNotConnectedError") { // not connected to chat } } ``` <Note> When a message is marked as read, the IDs of users who have read the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who read the message using the `list()` method. See [this section](/sdks/js-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Note> ## Send typing indicators You may want, as a sender, to let the recipient know that you are typing the message or have stopped typing the message. Use typing indicators as a form of chat-specific presence. Typing indicators allow to indicate if users are typing messages in a dialog at the moment. There are the following **typing** notifications supported. * **typing**. The user is composing a message. The user is actively interacting with a message input interface specific to this chat session (for example, by typing in the input area of a chat window). * **stopped**. The user had been composing but now has stopped. The user has been composing but has not interacted with the message input interface for a short period of time (for example, 30 seconds). To track the event when the sender is **typing** or **stopped typing** event, use the `QB.chat.onMessageListener`. See [this section](/sdks/js-chat-messaging#subscribe-message-events) to learn how to add the listener. As a result, when a user is typing or stopped typing a message, the SDK receives the `onMessageTypingListener()` callback. ```JavaScript JavaScript theme={null} QB.chat.onMessageTypingListener = function (isTyping, userId, dialogId) { if (isTyping) { // the user(userId) is typing in the chat(dialogId) } else { // the user stopped typing } }; ``` To notify a recipient that a sender is typing the message, use the `sendIsTypingStatus()` method. As a result, the server will notify a recipient about the event. ```JavaScript JavaScript theme={null} try { QB.chat.sendIsTypingStatus(currentDialog.jidOrUserId); } catch (e) { if (e.name === "ChatNotConnectedError") { // not connected to chat } } ``` To notify a recipient that a sender had been composing a message but now has stopped, use the `sendIsStopTypingStatus()` method. As a result, the server will notify a recipient about the event. ```JavaScript JavaScript theme={null} try { QB.chat.sendIsStopTypingStatus(currentDialog.jidOrUserId); } catch (e) { if (e.name === "ChatNotConnectedError") { // not connected to chat } } ``` | Argument | Required | Description | | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | jid | yes | Room JID. JID (Jabber ID) of XMPP room in the XMPP server. Pass room JID for type=2 (group dialog). Generated automatically by the server after dialog creation. You can get JID from the dialog ID. The JID format is the following: `<app_id>-<dialog_id>@muc.chat.quickblox.com"dialog`). | | userID | yes | ID of the opponent. Pass opponet ID for type=3 (private dialog). | ## Send system messages There is a way to send system messages to other users about some events. For example, a system message can be sent when a user has joined or left a group dialog. These messages are handled over a separate channel and are not be mixed up with regular chat messages. Thus, they are handled by the `onSystemMessageListener` callback. System messages are also not shown in the dialog history and, consequently, are not stored on the server. This means that these messages will be delivered **only** to online users. Send system messages using the `sendSystemMessage()` method. ```JavaScript JavaScript theme={null} var message = { body: "Notification message", extension: { param1: "value1", param2: "value2", }, }; var opponentId = 34; try { QB.chat.sendSystemMessage(opponentId, message); // or message.id = QB.chat.sendSystemMessage(opponentId, message) if message ID is needed } catch (e) { if (e.name === "ChatNotConnectedError") { // not connected to chat } } QB.chat.onSystemMessageListener = function (receivedMessage) {}; ``` | Argument | Description | | ---------- | ------------------------------------------------------- | | opponentId | ID of the opponent. | | message | Specifies system message fields that should be updated. | Set the following fields of the `message`: | Fields | Required | Description | | --------- | -------- | -------------------------------------------------------------------------------------------------------- | | body | no | A system notification text. | | extension | no | Extra data. Specify one or more key-value pairs. In each pair, the key and value are both string values. | # Offline Messaging Source: https://docs.quickblox.com/sdks/js-chat-offline-messaging Learn how to receive push notifications when the app is not running. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/js-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Enable offline messaging When you send a chat message and the recipient is offline, then an automatic push notification will be fired. To receive push notifications you need to subscribe for them. Please refer to [Push Notifications](/sdks/js-push-notifications) guide. To configure push template that users receive, go to **Dashboard => *YOUR\_APP* => Chat => Offline messaging**. <Warning> FCM data messages can be sent to the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages on your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notification. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> <Warning> Currently, push notifications are supported in a mobile environment only. </Warning> # Privacy List Source: https://docs.quickblox.com/sdks/js-chat-privacy-list Learn how to create privacy lists and implement user-to-user blocks. Privacy list API allows enabling or disabling communication with other users in a chat. You can create, modify, or delete privacy lists, define a default list. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. 3. Connect to the Chat server. See [Connection](/sdks/js-chat-connection) page to learn how to do it. <Note> The user can have multiple privacy lists, but only one can be active. </Note> ## Create privacy list A privacy list must have at least one element in order to be created. You can choose a type of blocked logic. There are 2 types: * Block in one way. When you blocked a user, but you can send messages to them. * Block in two ways. When you blocked a user, but you can't send messages to them. ```JavaScript JavaScript theme={null} var users = [ { user_id: 34, action: "deny" }, { user_id: 48, action: "deny", mutualBlock: true }, // it means you can't write to user { user_id: 18, action: "allow" } ]; var list = { name: "myList", items: users }; try { QB.chat.privacylist.create(list, function(error) {}); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` <Note> In order to be used the privacy list should be not only set but also activated (set as default). </Note> ## Activate privacy list In order to activate rules from a privacy list you should set it as default. ```JavaScript JavaScript theme={null} try { QB.chat.privacylist.setAsDefault("myList", function(error) { }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` ## Update privacy list There is a rule you should follow to update a privacy list: if you want to update or set a new privacy list instead of the current one, you should decline the current default list first. ```JavaScript JavaScript theme={null} try { QB.chat.privacylist.setAsDefault(null, function(error) { if (!error) { var users = [{ user_id: 34, action: "allow" }]; var list = { name: "myList", items: users }; QB.chat.privacylist.update(list, function(error) { if (!error) { QB.chat.privacylist.setAsDefault("myList", function(error) {}); } }); } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` ## Retrieve privacy lists To get a list of **all** your privacy lists names, use the following request. ```JavaScript JavaScript theme={null} try { QB.chat.privacylist.getNames(function(error, result) { if (!error) { var names = result.names; } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` ## Retrieve privacy list by name To get the privacy list by name, you should use the following method. ```JavaScript JavaScript theme={null} try { QB.chat.privacylist.getList("myList", function(error, result) { if (!error) { var name = result.name; var items = result.items; } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` ## Remove privacy list To delete a list, you can call a method below or you can edit a list and set items to `nil`. ```JavaScript JavaScript theme={null} try { QB.chat.privacylist.delete("myList", function(error) { }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` ## Blocked user attempts to communicate with user A user can be blocked in **1-1 dialog** and **group dialog**. In this case, the blocked user receives an error when trying to send a message in a **1-1 dialog** and receives nothing when trying to send a message in **group dialog**. ```JavaScript JavaScript theme={null} QB.chat.onMessageErrorListener = function (messageId, error){ } ``` # User Presence Source: https://docs.quickblox.com/sdks/js-chat-user-presence Learn how to track user presence updates and check user status using ping. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/js-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Subscribe to contact presence updates You can track contact list updates in real-time by using the `onContactListListener`. However, you can track the presence updates of only those users who have been added to the contact list. See [this section](/sdks/js-chat-contact-list) to learn how to implement the Contact list. ```JavaScript JavaScript theme={null} QB.chat.onContactListListener = function(userId, type) { // type - if a user left the chat, type will be 'unavailable'. // Otherwise - 'available'. }; ``` ## Ping user QuickBlox SDK can send application-level pings to a user. As a result, you can check if the user is connected to the Chat server. **Ping user by ID** ```JavaScript JavaScript theme={null} // ping user (by user ID) try { QB.chat.ping(userId, function (error) { if (error) { // no pong received } else { // pong received from user } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` | Argument | Required | Description | | ---------- | -------- | ----------------------------------------------------------------------------------------------- | | userId | yes | ID of the user. | | function() | yes | Specifies a callback function that accepts one argument that specifies what error has happened. | **Ping user by JID** ```JavaScript JavaScript theme={null} // ping user (by user JID) try { QB.chat.ping(userJid, function (error) { if (error) { // no pong received } else { // pong received from user } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` | Argument | Required | Description | | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | userJid | yes | User JID. JID (Jabber ID) of the user in the in the XMPP server. Assigned automatically by the server upon the login to the Chat. The users will have different JIDs for different apps. The JID format is the following:`<user_id>-<app_id>@chat.quickblox.com` | | function() | yes | Specifies a callback function that accepts one argument that specifies what error has happened. | ## Ping server QuickBlox SDK can send application-level pings to a server. As a result, you can check if there is a connection with the Chat server. ```JavaScript JavaScript theme={null} // ping server (some specific chat server) var server = 'chat.quickblox.com'; try { QB.chat.ping(server, function (error) { if (error) { // no pong received } else { // pong received from server } }); } catch (e) { if (e.name === 'ChatNotConnectedError') { // not connected to chat } } ``` | Argument | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | server | no | Custom server address. You can ping a specific chat server. If no server is specified, the server address is taken from the config object. See the [Point SDK to enterprise server](/sdks/js-setup#point-sdk-to-enterprise-server) section to learn how to set up custom server endpoints. | | function() | yes | Specifies a callback function that accepts one argument that specifies what error has happened. | ## Set ping timeout To control how much time it takes to respond to a ping, you should set a `pingTimeout`. **By default**, the `pingTimeout` is **30** seconds. If the response wasn't received within the specified time frame, then the error callback is called. Set the `pingTimeout` in seconds, in the `CONFIG` object. See [Initialize SDK](/sdks/js-setup#initialize-quickblox-sdk) section to learn more about other configuration options. ```JavaScript JavaScript theme={null} var APPLICATION_ID = "41"; var AUTH_KEY = "lkjdueksu7392kj"; var AUTH_SECRET = "iiohfdija792hj"; var ACCOUNT_KEY = "sdjfnksnlk2bk1k34kb"; var CONFIG = { // other configs pingTimeout: 3 }; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY, CONFIG); ``` # Content Source: https://docs.quickblox.com/sdks/js-content Learn how to store and access files with QuickBlox file storage. The content module allows storing rich chat attachments, app content, and settings without having to republish them. Using a web interface you or your clients can control and make instant changes to the apps. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. ## Retrieve files Get a list of files for a current user using the code snippet below. ```JavaScript JavaScript theme={null} // these parameters are optional var params = { page: 1, per_page: 10 }; QB.content.list(params, function(error, data) { // ... }); // equivalent of the above code QB.content.list(function(error, data) { // ... }); ``` ## Upload file Upload a file to the cloud storage using the following code snippet. ```JavaScript JavaScript theme={null} var input = document.querySelector("input[type=file]"); var file = input.files[0]; var params = { name: file.name, file: file, type: file.type, size: file.size, public: false // optional, "false" by default }; QB.content.createAndUpload(params, function(error, result) { if (error) { console.log(error); } else { console.log(result); var uploadedFile = result; var uploadedFileId = result.uid; } }); ``` The maximum size of the uploaded file depends on the membership plan. | Basic | Startup | Growth | HIPAA | Enterprise | | | --------------- | ------- | ------ | ----- | ---------- | --------------------------------------------------------------- | | File size limit | 10 Mb | 25 Mb | 50Mb | 50Mb | [Contact our sales team](https://quickblox.com/enterprise/#get) | ## Update file You can update a file name using the `update()` method. ```JavaScript JavaScript theme={null} var params = { id: 101, name: "GreatPlace", // new file name }; QB.content.update(params, function(error, result) { if (error) { console.log(error); } else { console.log(result); } }); ``` Set the following fields of the `params`: | Field | Required | Description | | ----- | -------- | --------------- | | id | yes | ID of the file. | | name | yes | File name. | ## Download file by UID If the file is public then it's possible to download it without a session token. ```JavaScript JavaScript theme={null} QB.content.getFile('file uid', function (error, file) { // ... }) ``` ## Get file info Get information about a file by ID using the `getInfo()` method below. This method allows to load the file description model from the server, not the content of the file itself. ```JavaScript JavaScript theme={null} var fileId = 1441441; QB.content.getInfo(fileId, function(error, result) { // ... }); ``` | Argument | Required | Description | | ---------- | -------- | ---------------------------------------------------------------------------- | | fileId | yes | ID of the file. | | function() | yes | Specifies a callback function that accepts that accepts an error and result. | ## Get file URL Except for downloading, you can also get URL of each file. There are two types of file URLs that can be obtained: private and public. * **Public URL** allows anyone to access the file, no authorization token is needed. * **Private URL** can be accessed only by QuickBlox user with a session token. ### Get public URL To receive a public URL, use the code snippet below. ```JavaScript JavaScript theme={null} var fileUID = 'file uid'; var fileUrl = QB.content.publicUrl(fileUID); ``` | Argument | Required | Description | | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- | | fileUID | yes | File unique identifier. You can take the fileUID from the object received as a result of the createAndUpload() or getInfo() method. | ### Get private URL To get a private URL of the uploaded file, use the following code snippet. ```JavaScript JavaScript theme={null} var fileUID = 'file uid'; var fileUrl = QB.content.privateUrl(fileUID); ``` | Argument | Required | Description | | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- | | fileUID | yes | File unique identifier. You can take the fileUID from the object received as a result of the createAndUpload() or getInfo() method. | ## Delete file Delete a file by file ID using the `delete()` method below. ```JavaScript JavaScript theme={null} var fileId = 1441441; QB.content.delete(fileId, function(error, result) { // ... }); ``` | Argument | Required | Description | | ---------- | -------- | ---------------------------------------------------------------------------- | | fileId | yes | ID of the file. | | function() | yes | Specifies a callback function that accepts that accepts an error and result. | # Custom Objects Source: https://docs.quickblox.com/sdks/js-custom-objects Learn how to store and sync data with QuickBlox key-value storage. Custom Objects module provides flexibility to define any data structure (schema) you need, build one-to-many relations between schemas and control permissions for all operations made on data. Schema is defined in QuickBlox Dashboard. There are two key concepts in Custom Objects: \- **Class** represents your schema and contains field names and types. \- **Record** represents the data you put into your schema. **Class** and **Record** are similar to table and row in relational database. Every class in Custom Object module comes with five mandatory predefined fields: `_id`, `user_id`, `parent_id`, `created_at`, and `updated_at`. Allowed data types: **Integer** (or Array of Integer); **String** (or Array of String); **Float** (or Array of Float); **Boolean** (or Array of Boolean); **Location** (Array of \[\< longitude >, \< latitude >]); **File**; **Date**. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. ## Create class To start using Custom Objects module, create a class: 1. Go to [QuickBlox Dashboard](https://admin.quickblox.com/signin). 2. Follow **Custom => Add => Add new class** direction. As a result, **Add new class** popup will appear. 3. Enter a class name, add any fields you want. <Frame> <img alt="js-custom-add-filed.png" /> </Frame> 1. Click **Create class button** to create a new class. <Frame> <img alt="js-custom-fields.png" /> </Frame> ## Create records Here is the easiest way to create a new record from the [QuickBlox Dashboard](https://admin.quickblox.com/signin): 1. Follow **Custom => Current class => Your Class** direction. 2. Click **Add => Add record** button. 3. Fill in any fields you want. 4. Click **Add record button** and a new record will be added and shown in the table. You can also create a new record/records using the `create()` method. To create a single record, use the code snippet below. ```JavaScript JavaScript theme={null} var className = 'GameOfThrones'; var data = { name: 'John', age: 20, family: [ 'Stark', 'Targaryen' ] }; QB.data.create(className, data, function (error, result) { // ... }); ``` To create multiple records, use the code snippet below. ```JavaScript JavaScript theme={null} var className = "GameOfThrones/multi"; var data = { record: { 0: { name: "John", age: 20, family: ["Stark", "Targaryen"], }, 1: { name: "Daenerys", age: 21, family: ["Targaryen"], }, }, }; QB.data.create(className, data, function (error, result) { // ... }); ``` | Argument | Required | Description | | ---------- | -------- | --------------------------------------------------------------- | | className | yes | A name of a custom object class. | | data | yes | Data records that will be created. | | function() | yes | Specifies a callback function that accepts an error and result. | ## Retrieve records by IDs You can get records with a particular record ID using the `list()` method. ```JavaScript JavaScript theme={null} var className = "Note"; var ids = ["53aaa06f535c12cea9007496", "53aaa06f535c12cea9007495"]; // use filter or ids to get records: QB.data.list(className, ids, function (error, result) { if (error) { console.log(error); } else { console.log(result); } }); ``` | Argument | Required | Descriptions | | ---------- | -------- | --------------------------------------------------------------- | | className | yes | A name of a custom object class. | | ids | yes | Records IDs. | | function() | yes | Specifies a callback function that accepts an error and result. | ## Retrieve records You can search for records of a particular class. The request below will return records of the `Note` class with specific IDs in the array. ```JavaScript JavaScript theme={null} var className = "Note"; var filter = { _id: { in: ["5f59b10fa0eb4772bd5e9976", "5f59a76ca28f9a28032944d3"], }, }; QB.data.list(className, filter, function (error, result) { if (error) { } else { } }); ``` | Argument | Required | Description | | ---------- | -------- | --------------------------------------------------------------- | | className | yes | A name of a custom object class. | | filter | yes | Specifies filtering criteria for the field. | | function() | yes | Specifies a callback function that accepts an error and result. | If you want to retrieve only records updated after some specific date time and order the search results, you can apply operators. Thus, you can apply [search](/sdks/js-custom-objects#search-operators) and [sort](/sdks/js-custom-objects#sort-operators) operators to list records on the page so that it is easier to view specific records. If you want to get a paginated list of dialogs from the server, you can set the following fields of the `filter`: | Field | Required | Description | | ----- | -------- | ----------------------------------------------------------------------------------------------------------------- | | skip | no | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. Should be an Integer. | | limit | no | Limit search results to N records. Useful for pagination. Default value: **100**. | ### Search operators You can use search operators to get more specific search results. The request below will return two records of the `GameOfThrones` by the `age` field with a value greater than `20`, limited to 2 records on the page. ```JavaScript JavaScript theme={null} let className = "GameOfThrones"; let filter = { 'age': { 'gt': 20 }, limit:2 }; QB.data.list(className, filter, function(err, result){ }); ``` Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Description | | ---------------- | ------------------------------- | ---------------------------------------------------- | | lt | integer, float | **Less Than** operator. | | lte | integer, float | **Less Than** or **Equal** to operator. | | gt | integer, float | **Greater Than** operator. | | gte | integer, float | **Greater Than** or **Equal** to operator. | | ne | integer, float, string, boolean | **Not Equal** to operator. | | in | integer, float, string | **IN** array operator. | | nin | integer, float, string | Not **IN** array operator. | | all | array | **ALL** are contained in array. | | or | integer, float, string | All records that contain a value 1 **or** value 2. | | ctn | string | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return records of `GameOfThrones` class sorted in descending order by the `created_at` field. ```JavaScript JavaScript theme={null} let className = "GameOfThrones"; let filter = { sort_desc: "created_at" }; QB.data.list(className, filter, function(err, result){ }); ``` Here are the sort options that you can use to sort the search results. | Sort options | Applicable to types | Description | | ------------ | ------------------- | ------------------------------------------------------------------------- | | sort\_asc | All types | Search results will be sorted in ascending order by the specified field. | | sort\_desc | All types | Search results will be sorted in descending order by the specified field. | ### Aggregation operators You can use an aggregation operator to count search results. The request below will return a count of all records of the `GameOfThrones` class. ```JavaScript JavaScript theme={null} let className = "GameOfThrones"; let filter = { count:1 }; QB.data.list(className, filter, function(err, result){ }); ``` Here are the aggregation operators you can use to count records: | Aggregation operator | Description | | -------------------- | ------------------------------------------------------------------------------------------------------- | | count | Count search results. The response will contain only a count of records found. Set count to 1 to apply. | ## Update records You can update a single record using the `update()` method. You should know the record ID in this case. ```JavaScript JavaScript theme={null} var className = 'GameOfThrones'; var data = { _id: '53aaa06f535c12cea9007496', name: 'John', age: 21, family: ['Stark'] }; QB.data.update(className, data, function(error, result) { // ... }); ``` You can update multiple records using the code snippet below. ```JavaScript JavaScript theme={null} var className = 'GameOfThrones/multi'; var data = { "record":{ "0":{ name: 'John', age: 20, family: [ 'Stark', 'Targaryen' ] }, "1":{ name: 'Daenerys', age: 21, family: [ 'Targaryen' ] } } }; QB.data.update(className, data, function (error, result) { // ... }); ``` ## Delete records To delete a record/records, use the `delete()` method. To delete a single record, use the code snippet below. ```JavaScript JavaScript theme={null} var className = "Movie"; // By ID var id = "502f7c4036c9ae2163000002"; QB.data.delete(className, id, function(error, result) { // ... }); ``` | Argument | Required | Description | | ---------- | -------- | --------------------------------------------------------------- | | className | yes | A name of a custom object class. | | id | yes | ID of the record. | | function() | yes | Specifies a callback function that accepts an error and result. | To delete multiple records, use the code snippet below. ```JavaScript JavaScript theme={null} var className = "Movie"; // By IDs var ids = ["502f7c4036c9ae2163000002", "502f7c4036c9ae2163000003"]; QB.data.delete(className, ids, function(error, result) { // ... }); ``` | Argument | Required | Description | | ---------- | -------- | --------------------------------------------------------------- | | className | yes | A name of a custom object class. | | ids | yes | Records IDs. | | function() | yes | Specifies a callback function that accepts an error and result. | To delete a record by criteria, use the code snippet below. ```JavaScript JavaScript theme={null} var className = "Movie"; // By criteria var criteria = { price: { gt: 100 } }; QB.data.delete(className, criteria, function(error, result) { // ... }); ``` | Argument | Required | Description | | ---------- | -------- | --------------------------------------------------------------- | | className | yes | A name of a custom object class. | | criteria | yes | Specifies criteria fields that should be set. | | function() | yes | Specifies a callback function that accepts an error and result. | ## Relations It is possible to create a relation between objects of **two different classes** via `_parent_id` field. For example, we have the class **Rating** that contains `score`, `review`, and `comment` fields. We also have a **Movie** class. So we can create a record of class **Rating** that will point to the record of the class **Movie** via its `_parent_id` field, so the `_parent_id` field will contain the ID of record from class **Movie**. <Warning> This is not a simple soft link. This is actually a **hard link**. When you delete the **Movie** class record then all its children (records of class **Rating** with `_parent_id` field set to the **Movie** class record ID) will be automatically **deleted** as well. </Warning> <Tip> If you need to retrieve all children, you can retrieve records with the filter `_parent_id=<id_of_parent_class_record>`. </Tip> ## Permissions Access control list (ACL) is a list of permissions attached to some object. An ACL specifies which users have access to objects as well as what operations are allowed on given objects. Each entry in a typical ACL specifies a subject and an operation. ACL models may be applied to collections of objects as well as to individual entities within the system hierarchy. <Tip> Access Control list available **only** for Custom Objects module. </Tip> ### Permission schema QuickBlox Permission schema contains **five** permissions levels: * **Open (open)** Such permission schema means that any user within the application can access the record/records in the class and is allowed to perform an action with the current permission level. * **Owner (owner)** Owner permission level means that only Owner (a user who created a record) is allowed to perform action with the current permission level. * **Not allowed (not\_allowed)** No one (except for the Account Administrator) can make a chosen action. * **Open for groups (open\_for\_groups)** Users having a specified tag/tags (see more info about how to set tags for the user in [Users](/sdks/js-users) will be included in the group that is allowed to perform an action with the current permission level. The current permission level can consist of one or several groups (number of groups is **not** limited). Tags can be added/deleted in the user’s profile. * **Open for user ids (open\_for\_users\_ids)** Only users that are specified in the permission level can make a required action with a record. One or several users can be specified (the number of users is **not** limited). ### Actions available for the entity * **Create** Create a record. * **Read** Retrieve and read the info about the chosen record. * **Update** Update any parameter for the chosen record (only those parameters that can be set by the user can be updated). * **Delete** Delete a record. ### Permission levels There are two access levels in the Permissions schema: **Class** and **Record**. #### Class entity Only the Account Administrator can create a class in the Custom object module and make all possible actions with it. Operations with **Class** entity are not allowed in API. All actions (Create, Read, Update, and Delete) are available for the class entity and are applicable for all records in the class. Every action has a separate permission level available. The exception is a Create action that is not available for the **Owner** permission level. To set a permission schema for the Class, do the following: 1. Go to the **Custom Objects** tab. 2. Open a required class. 3. Click **Edit permissions** button to open a class and edit it. <Frame> <img alt="js-custom-permissions.png" /> </Frame> Default Class permission schema is used while creating a class: * **Create**: Open * **Read**: Open * **Update**: Owner * **Delete**: Owner <Note> Mark checkboxes to **enable** class permissions. </Note> #### Record entity A record is an entity within the class in the Custom Objects module that has its own permission levels. You can create a record in the Dashboard and API (see [Create Record](#section-create-records) request for more details). All permission levels except for the **Not Allowed** are available for the record and there are only three actions available and applicable for the record: Read, Update, and Delete. Default Record permission schema is used while creating a class: * **Read**: Open * **Update**: Owner * **Delete**: Owner To set a permission level open the required Class and click the record to edit it. <Frame> <img alt="js-custom-edit-record.png" /> </Frame> ### Choosing a permission schema Only **one** permission level can be applicable to the record: class permission schema or record permission schema. To apply class permission levels to **all** records in the class, tick the checkbox in the **Use Class permissions** column near the required Action in the Dashboard. <Frame> <img alt="js-custom-edit-permissions.png" /> </Frame> <Note> Using a class permission schema means that a record permission schema will **not** affect a reсord. </Note> <Tip> In case, the Admin **does not** tick the checkbox in the Dashboard a user **has** a possibility to change permission levels for every separate record in the table or create a new one with the ACL that a user requires. </Tip> ### Create record with permissions Let's create a record with the next permissions: * READ: Open. * UPDATE: Users in groups **golf**, **man**. * DELETE: Users with IDs **3060**, **63635**. ```JavaScript JavaScript theme={null} var className = "Blog"; var permissions = { read: { access: "open" }, update: { access: "open_for_groups", groups: ["golf", "man"] }, delete: { access: "open_for_users_ids", ids: [3060, 63635] } }; var params = { name: "Star Wars", genre: "fantasy", permissions: permissions }; QB.data.create(className, params, function(error, result) { if (error) { console.log(error); } else { console.log(result); } }); ``` ### Update record permissions Let's update record permissions to next: * READ: Users in groups **car, developers**. * UPDATE: Owner. * DELETE: Owner. ```JavaScript JavaScript theme={null} var className = "Blog"; var permissions = { read: { access: "open_for_groups", groups: ["car", "developers"] , update: { access: "owner" }, delete: { access: "owner" } }; var payload = { name: "Star Wars", genre: "fantasy", permissions: permissions }; QB.data.update(className, payload, function(error, result) { // ... }); ``` | Argument | Required | Description | | ---------- | -------- | --------------------------------------------------------------- | | className | yes | A name of a custom object class. | | payload | yes | Specifies payload fileds that should be set. | | function() | yes | Specifies a callback function that accepts an error and result. | ## Files Custom Objects module supports the `File` field type. It is created to work easily with content from the Custom Objects module. There is an ability to upload, download, update and delete the content of file fields. ## Upload/Update file Use the code lines below to upload/update a file. ```JavaScript JavaScript theme={null} var className = "Blog"; var recordId = "42453312753abce36783882222"; var fileInput = document.querySelector("input[type=file]"); var file = fileInput.files[0]; var params = { id: recordId, field_name: "user_avatar", file: file, name: file.name }; QB.data.uploadFile(className, params, function(error, result) { if (error) { console.log(error); } else { console.log(result); } }); ``` ## Download file To download a file, use the code snippet below. ```JavaScript JavaScript theme={null} var className = "Movie"; var paramsFor = { id: "1734252234523893bae6739922339984a3bb", field_name: "poster" }; var fileUrl = QB.data.fileUrl(className, paramsFor); var anchorHTML = '<a href="' + fileUrl + '" download>Download</a>'; ``` ## Delete file To delete a file, use the code snippet below. ```JavaScript JavaScript theme={null} var className = "Blog"; var recordId = "42453312753abce36783882222"; var params = { id: recordId, field_name: "user_avatar" }; QB.data.deleteFile(className, params, function(error, result){ if (error) { console.log(error); } else { console.log(result); } }); ``` # Push Notifications Source: https://docs.quickblox.com/sdks/js-push-notifications Learn how to send push notifications to users when they are offline. Push Notifications provide a way to deliver some information to a user while they are not using your app actively. The following use cases can be covered by push notifications: * **Offline messages.** Send a chat message when a recipient is offline. In this case, a push notification will be sent automatically if the user is offline. * **Offline calls.** Make a video/audio call with offline opponents. In this case, a push notification will be sent manually. * **Requests to contact list.** Send requests to add a user to the contact list. In this case, a push notification will be sent manually). * **User tags.** Send notifications to specific user groups defined by tags. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. ## Configuration In order to start working with push notifications, you need to configure it. * For iOS application that uses Apple Push Notifications, you must have **APNs certificate** uploaded via the QuickBlox Dashboard Console panel. Review [this page](/sdks/ios-push-notifications) for more details. * For Android application that uses **Firebase Cloud Messaging (FCM)** you need to obtain **API Key** and set it on QuickBlox Dashboard Console panel. Review [this page](/sdks/android-push-notifications) for more details. ## Send push notifications You can manually initiate sending push notifications to user/users on any event in your application. To do so, you need to form push notification parameters (payload) and set the push recipients. ```JavaScript JavaScript theme={null} var payload = JSON.stringify({ message: "New market products are available", ios_voip: "1", VOIPCall: "1" }); var pushParameters = { notification_type: "push", user: { ids: [21, 12] }, // recipients. environment: "development", // environment, can be 'production'. message: QB.pushnotifications.base64Encode(payload) }; QB.pushnotifications.events.create(pushParameters, function(error, result) { if (error) { console.log(error); } else { // success console.log("Push Notification is sent."); } }); ``` <Warning> You can send only FCM data messages to the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages on your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notification. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> <Note> You can send APNs VoIP notifications from the web app to the iOS app. However, if the iOS app is not subscribed to APNs VoIP notifications or the APNs VoIP certificate has expired, the regular APNs will be delivered instead of APNs VoIP. </Note> # Quick Start Source: https://docs.quickblox.com/sdks/js-quick-start Learn how to install QuickBlox SDK and send your first message. QuickBlox SDK helps you implement real-time chat, video chat, and push notifications to your app. You can fully concentrate on your mobile app development. QuickBlox JavaScript SDK can be used for web development solely or with all popular libraries like **ReactJS**, **Angular**, etc, for chatbots development on **Node.js** and mobile development on **Cordova**. ## Start with sample apps If you are just starting your app and developing it from scratch, we recommend to use our sample apps. We use GitHub repositories to make it easy to explore, copy, and modify our code samples. The guide on how to launch and configure the sample app is on GitHub. ### Chat samples Choose the code sample below to jump-start the development. <CardGroup> <Card title="JavaScript Chat Sample App" icon="js"> <a href="https://sample.quickblox.com/chat/#!/login"> <Icon icon="arrow-up-right-from-square" /> Live Demo </a> <br /> <a href="https://github.com/QuickBlox/quickblox-javascript-sdk/tree/gh-pages/samples/chat"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/js-chat"> <Icon icon="book" /> Documentation </a> </Card> <Card title="Angular Chat Sample App" icon="angular"> <a href="https://github.com/QuickBlox/quickblox-javascript-sdk/tree/gh-pages/samples/angular-chat"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/js-chat"> <Icon icon="book" /> Documentation </a> </Card> </CardGroup> ### Video Chat Samples Choose the code sample below to jump-start the development. <CardGroup> <Card title="JavaScript Video Calling Sample App" icon="js"> <a href="https://sample.quickblox.com/webrtc/#join"> <Icon icon="arrow-up-right-from-square" /> Live Demo </a> <br /> <a href="https://github.com/QuickBlox/quickblox-javascript-sdk/tree/gh-pages/samples/webrtc"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/js-video-calling"> <Icon icon="book" /> Documentation </a> </Card> </CardGroup> ### More samples For more samples, head to our [Code Samples](/code-samples/code-samples) page. These sample apps are available on GitHub so feel free to browse them there. Just clone the repository and modify the source code for your own projects. ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app clicking **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click **Add** button. 4. Go to **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key**. ## Requirements The minimum requirements for QuickBlox JavaScript SDK are: * JavaScript es5 ## Install QuickBlox SDK into your app There are 3 ways to integrate QuickBlox JavaScript SDK into your app. ### Dependencies for browser Install QuickBlox library dependencies for browser to establish communication with QuickBlox server. Simply connect the JS file as a normal script. Once it is done, a window scoped variable called `QB` is created. ```HTML HTML theme={null} <script src="https://unpkg.com/quickblox/quickblox.min.js"></script> ``` ### Node.js and npm integration <Note> To manage project dependencies [Node.js](https://nodejs.org/en/) and [npm](https://docs.npmjs.com/getting-started) must be installed. </Note> 1. Open a terminal and enter the commands below in your project path. ```Bash Bash theme={null} npm install quickblox --save ``` 1. To be able to work with QuickBlox library, connect it as follows: ```JavaScript JavaScript theme={null} var QuickBlox = require('quickblox/quickblox.min'); // OR to create many QB instances var QuickBlox = require('quickblox/quickblox.min').QuickBlox; var QB1 = new QuickBlox(); var QB2 = new QuickBlox(); ``` ### Yarn integration Install the SDK through Yarn by running the following command: ```Bash Bash theme={null} yarn add quickblox ``` ## Send your first message ### Initialize QuickBlox SDK Initialize the SDK with application credentials. Call the `init()` method and pass the `APPLICATION_ID`, `AUTH_KEY`, `AUTH_SECRET`, and `ACCOUNT_KEY` as arguments to it. ```JavaScript JavaScript theme={null} var APPLICATION_ID = 41; var AUTH_KEY = "lkjdueksu7392kj"; var AUTH_SECRET = "iiohfdija792hj"; var ACCOUNT_KEY = "sdjfnksnlk2bk1k34kb"; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY); ``` <Warning> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend and initialize QuickBlox SDK with applicationId and acountKey only. More details you can find in [Initialize QuickBlox SDK without Authorization Key and Secret](/sdks/js-setup#install-quickblox-sdk-into-your-app) section. </Warning> ### Authorize user Now, it is time to log in with the user. To get it done, set the login and password in the `params` variable. Call the `createSession()` method and pass the `params` as an argument to it using the code snippet below ```JavaScript JavaScript theme={null} var params = { login: "garry", password: "garry5santos" }; QB.createSession(params, function(error, result) { // callback function }); ``` ### Connect to chat Having authorized a user, you can proceed with connecting to the chat server to start using Chat module functionality. Set the `userId` and `password` fields in the `userCredentials`. Call the `connect()` method and pass the `userCredentials` as an argument to it. ```JavaScript JavaScript theme={null} var userCredentials = { userId: 12345, password: "garry5santos" }; QB.chat.connect(userCredentials, function(error, contactList) { }); ``` ### Create dialog QuickBlox provides three types of dialogs: **1-1 dialog**, **group dialog**, and **public dialog**. Learn more about dialogs [here](/sdks/js-chat-dialogs#create-dialog). Let’s create a **1-1 dialog**. Set the `type` and `occupants_ids` properties of the `params` variable. Call the `create()` method and pass the `params` as an argument. ```JavaScript JavaScript theme={null} var params = { type: 3, occupants_ids: [56] }; QB.chat.dialog.create(params, function(error, dialog) { }); ``` ### Subscribe to receive messages Through the `QB.chat.onMessageListener` you can monitor whether an incoming message is received from the QuickBlox server. Use the `QB.chat.onMessageListener` to listen to all incoming messages. ```JavaScript JavaScript theme={null} function onMessage(userId, message) { }; QB.chat.onMessageListener = onMessage; ``` ### Send message To send a message, create a `message` variable and set the `type`, `body`, and `extension` properties. Create `opponentId` variable and set the ID of the user we are going to send a message to. Call the `send()` method and pass the created `message` and `opponentId` as arguments to it. ```JavaScript JavaScript theme={null} var dialog = "..."; var message = { type: "chat", body: "How are you today?", extension: { save_to_history: 1, dialog_id: dialog._id }, markable: 1 }; var opponentId = 78; message.id = QB.chat.send(opponentId, message); // ... ``` <Note> Set the `save_to_history` parameter if you want this message to be saved in chat history. </Note> # AI Features Source: https://docs.quickblox.com/sdks/js-sdk-ai-features AI Features base on AI Agents ## Overview Starting from version 2.17.0 of JavaScript SDK, the AI functionality is enabled and based on AI Agents. The QuickBlox JavaScript SDK provides a range of features to enhance the chat experience. With essential messaging functionalities such as answer assistant, users can engage in more interactive conversations. **Supported features** | Name | Description | Min SDK Version | | ---------------- | -------------------------------------------------------------------------- | --------------- | | AI Answer Assist | Generates a draft response based on chat history for the selected message. | 2.17.0 | | AI Translate | Provides translation based on chat history to selected incoming message. | 2.17.0 | | AI Gateway | Multimodal AI with text and images support (OpenAI-compatible format). | 2.21.5 | | AI Summarize | Generates a summary of dialog messages (up to 1000 recent messages). | 2.21.5 | ## Requirements The minimum requirements for using AI features are: * JS QuickBlox SDK v2.17.0 (for AI Answer Assist and AI Translate) * JS QuickBlox SDK v2.21.5 (for AI Gateway and AI Summarize) * QuickBlox account with activated AI Agents Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. Visit [AI Agent overview](/ai-agent/overview) page to learn the most important AI Agents concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. 4. Create or update your AI Agent. See [AI Agent](/ai-agent/manage-agents#create-ai-agent) documentation to learn how to do it. ### Enable AI Extensions 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Extensions** page. 2. Select the checkboxes for the features you want to enable. 3. Click the **Save** button to save changes. <Frame> <img alt="Enable AI Extensions" /> </Frame> ### Get AI Agent ID To get the **AI Agent ID**, navigate to the **Dashboard => *YOUR\_APP* => AI Agent** page. The ID is displayed in the list of assistants. <Frame> <img alt="AI Agent list with IDs" /> </Frame> ## AI Answer Assist QuickBlox AI Answer Assist generates a contextually relevant draft response based on the chat history. Use it to suggest replies that match the conversation context. ```JavaScript JavaScript theme={null} var smartChatAssistantId = '6633a1300fea600001bd6e71'; var messageToAssist = 'Where is my order?'; var history = [ {role: "user", message: "Hello"}, {role: "assistant", message: "Hi"} ]; QB.ai.answerAssist(smartChatAssistantId, messageToAssist, history, function (error, response) { if (error) { console.error('QB.ai.answerAssist: Error:', error); } else { console.log('QB.ai.answerAssist: Response:', response.answer); } }); ``` | Argument | Type | Description | | -------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual Smart Chat Assistant ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | | messageToAssist | String | Message you want to get answer for. | | history | Array of Object | Conversation history. Used to add context. Each object of array should have the two fields: 'role' and 'message'. The field role should contains one of next values: 'user' or 'assistant'. The field message should be a string with chat message. | | callback | function | The callback function. | ## AI Translate QuickBlox offers translation functionality that helps users easily translate text messages in chat, taking into account the context of the chat history. ```TypeScript TypeScript theme={null} var smartChatAssistantId = '6633a1300fea600001bd6e71'; var textToTranslate = 'Hola!'; var languageCode = 'en'; QB.ai.translate(smartChatAssistantId, textToTranslate, languageCode, function (error, response) { if (error) { console.error('QB.ai.translate: Error:', error); } else { console.log('QB.ai.translate: Response:', response.answer); } }); ``` | Argument | Type | Description | | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | smartChatAssistantId | String | This field should hold your actual Smart Chat Assistant ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | | textToTranslate | String | Text to translate. | | languageCode | String | Target language code in [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format (e.g., `en`, `es`, `fr`, `de`). See [AI Translate API](/reference/ai-extensions-ai-translate) for the list of supported languages. | | callback | function | The callback function. | ## AI Gateway <Note> **Available starting from SDK version 2.21.5** </Note> QuickBlox AI Gateway provides multimodal AI capabilities with support for text and images using OpenAI-compatible message format. This feature allows you to send complex requests including image recognition, multi-turn conversations, and system prompts. **Basic text message:** ```JavaScript JavaScript theme={null} var smartChatAssistantId = '6633a1300fea600001bd6e71'; var messages = [ { role: 'user', content: [ { type: 'text', text: 'Hello, what can you do?' } ] } ]; QB.ai.gateway(smartChatAssistantId, messages, function (error, response) { if (error) { console.error('QB.ai.gateway: Error:', error); } else { console.log('QB.ai.gateway: Response:', response.answer); } }); ``` **Image recognition:** ```JavaScript JavaScript theme={null} var smartChatAssistantId = '6633a1300fea600001bd6e71'; var messages = [ { role: 'user', content: [ { type: 'text', text: "What's in this image?" }, { type: 'image_url', image_url: { url: 'https://example.com/image.jpg' } } ] } ]; QB.ai.gateway(smartChatAssistantId, messages, function (error, response) { if (error) { console.error('QB.ai.gateway: Error:', error); } else { console.log('QB.ai.gateway: Response:', response.answer); } }); ``` **Multi-turn conversation with system prompt:** ```JavaScript JavaScript theme={null} var smartChatAssistantId = '6633a1300fea600001bd6e71'; var messages = [ { role: 'developer', content: [{ type: 'text', text: 'You are a helpful math tutor.' }] }, { role: 'user', content: [{ type: 'text', text: 'What is 2+2?' }] }, { role: 'assistant', content: [{ type: 'text', text: '2+2 equals 4.' }] }, { role: 'user', content: [{ type: 'text', text: 'And what is that multiplied by 3?' }] } ]; QB.ai.gateway(smartChatAssistantId, messages, function (error, response) { if (error) { console.error('QB.ai.gateway: Error:', error); } else { console.log('QB.ai.gateway: Response:', response.answer); } }); ``` **Arguments** | Argument | Type | Description | | -------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual Smart Chat Assistant ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | | messages | Array of Object | Array of message objects in OpenAI-compatible format. See message structure below. | | callback | function | The callback function. | **Message structure** Each message in the `messages` array should have the following structure: | Field | Type | Description | | ------- | --------------- | ----------------------------------------------------------------------- | | role | String | One of: `'user'`, `'assistant'`, or `'developer'` (for system prompts). | | content | Array of Object | Array of content items. Each item can be text or image\_url type. | **Content item types:** Text content: ```JavaScript theme={null} { type: 'text', text: 'Your message text' } ``` Image content: ```JavaScript theme={null} { type: 'image_url', image_url: { url: 'https://example.com/image.jpg' } } ``` <Tip> Images can be provided as URLs or base64 data URIs (e.g., `data:image/jpeg;base64,...`). </Tip> ## AI Summarize <Note> **Available starting from SDK version 2.21.5** </Note> QuickBlox AI Summarize generates a summary of dialog messages. This feature is useful for quickly understanding the context of a conversation without reading all messages. ```JavaScript JavaScript theme={null} var smartChatAssistantId = '6633a1300fea600001bd6e71'; var dialogId = '5f3e7a8b9c1d2e3f4a5b6c7d'; QB.ai.summarize(smartChatAssistantId, dialogId, function (error, response) { if (error) { console.error('QB.ai.summarize: Error:', error); } else { console.log('QB.ai.summarize: Summary:', response.summary); } }); ``` **Arguments** | Argument | Type | Description | | -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual Smart Chat Assistant ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | | dialogId | String | The ID of the dialog to summarize. | | callback | function | The callback function. | **Response** The response object contains a `summary` field with the generated summary text: ```JavaScript theme={null} { summary: "The conversation discussed project deadlines and task assignments..." } ``` <Note> * AI Summarize processes up to **1,000 most recent messages** in the dialog. * For empty dialogs, the response will be `{ summary: "Chat is empty." }`. * The user must have access to the dialog to summarize it. </Note> # SDK Reference Source: https://docs.quickblox.com/sdks/js-sdk-reference # Setup Source: https://docs.quickblox.com/sdks/js-setup Learn how to add and configure QuickBlox SDK for your app. Follow the instructions below to ensure that QuickBlox JavaScript SDK runs smoothly with your app. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Browsers support | Edge | Firefox | Chrome | Opera | Node.js | Safari | | ---- | ------- | ------ | ----- | ------- | ------ | | 14+ | 52+ | 50+ | 36+ | 6+ | 11.1+ | ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app by clicking the **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click the **Add** button. 4. Go to **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key**. ## Requirements The minimum requirements for QuickBlox JavaScript SDK are: * JavaScript es5 ## Install QuickBlox SDK into your app There are 3 ways to integrate QuickBlox JavaScript SDK into your app. ### Dependencies for browser Install QuickBlox library dependencies for browser to establish communication with QuickBlox server. Simply connect the JS file as a normal script. Once it is done, a window scoped variable called `QB` is created. ```HTML HTML theme={null} <script src="https://unpkg.com/quickblox/quickblox.min.js"></script> ``` ### Node.js and NPM integration <Note> To manage project dependencies [Node.js](https://nodejs.org/en/) and [npm](https://docs.npmjs.com/getting-started) must be installed. </Note> 1. Open a terminal and enter the commands below in your project path. ```Bash Bash theme={null} npm install quickblox --save ``` 1. To be able to work with QuickBlox library, connect it as follows: ```JavaScript JavaScript theme={null} var QuickBlox = require('quickblox'); // OR to create many QB instances var QuickBlox = require('quickblox').QuickBlox; var QB1 = new QuickBlox(); var QB2 = new QuickBlox(); ``` ### Yarn Install the SDK through Yarn by running the following command: ```Bash Bash theme={null} yarn add quickblox ``` ## Initialize QuickBlox SDK Initialize the framework with your application credentials. Pass **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** to the `init()` method. ```JavaScript JavaScript theme={null} var APPLICATION_ID = 41; var AUTH_KEY = "lkjdueksu7392kj"; var AUTH_SECRET = "iiohfdija792hj"; var ACCOUNT_KEY = "sdjfnksnlk2bk1k34kb"; var CONFIG = { debug: true }; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY, CONFIG); ``` <Warning> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend. </Warning> | Argument | Description | | --------------- | --------------------------------------------------------------------------------------------- | | APPLICATION\_ID | Application identifier. | | AUTH\_KEY | Authorization key. | | AUTH\_SECRET | Authorization secret. | | ACCOUNT\_KEY | Account key. Required to get actual Chat and API endpoints for the right server. | | CONFIG | **(optional)** SDK configuration settings for custom endpoints, chat connecting, WebRTC, etc. | You can set the following fields using the `CONFIG` object: | Field | Description | | ---------------- | -------------------------------------------------------------------------------------------------------- | | endpoints | [Custom endpoints](/sdks/js-setup#section-custom-endpoints) configuration. | | streamManagement | [Stream management](/sdks/js-setup#section-stream-management) configuration. | | webrtc | [WebRTC](/sdks/js-video-calling-advanced#section-video-calling-settings) configuration. | | chatProtocol | Chat protocol configuration. Set **1** to use BOSH, set **2** to use WebSockets. Default: **WebSocket**. | | debug | [Debug mode](/sdks/js-setup#enable-logging) configuration. | | endpoints | [Custom endpoints](/sdks/js-setup#section-custom-endpoints) configuration. | ## Initialize QuickBlox SDK without Authorization Key and Secret You may don't want to store **authKey** and **authSecret** inside an application for security reasons. In such case, you can initialize QuickBlox SDK with **applicationId** and **accountKey** only, and store your **authKey** and **authSecret** on your backend. But, if so, the implementation of [authentication with QuickBlox](https://docs.quickblox.com/reference/authentication) should be also moved to your backend. ```JavaScript JavaScript theme={null} var appId = 3451; var accountKey = 'sdjfnksnlk2bk1k34kb'; var config = { debug: false }; QB.initWithAppId(appId, accountKey, config); ``` Then using your backend you can authorize a user in the QuickBlox system, send back the user session token, and set it to the QuickBlox SDK using `startSessionWithToken(sessionToken, callBack)` method. You can find out more about this in the [Set existing session](/sdks/js-authentication#set-existing-session) section. ## Initialize with existing token <Warning> If you have version lower than 2.14.1, you have to use only the code below. The section [Initialize QuickBlox SDK without Authorization Key and Secret](/sdks/js-setup#install-quickblox-sdk-into-your-app) describe how to implement this for version greater than 2.14.1 </Warning> You can initialize the SDK with an existing QuickBlox token. It can be interesting in cases when you build a big system and you have the server side which generates QuickBlox tokens. ```JavaScript JavaScript theme={null} var sessionToken = '1b785b603a9ae88d9dfbd1fc0cca0335086927f1'; var appId = 3451; var accountKey = 'sdjfnksnlk2bk1k34kb'; var config = { debug: false }; QB.init(sessionToken, appId, null, accountKey, config); ``` ## Point SDK to enterprise server To point QuickBlox SDK to the QuickBlox enterprise server, you should set API and chat endpoints in the `CONFIG` object and pass it to the `init()` method. ```JavaScript JavaScript theme={null} var APPLICATION_ID = 41; var AUTH_KEY = "lkjdueksu7392kj"; var AUTH_SECRET = "iiohfdija792hj"; var ACCOUNT_KEY = "sdjfnksnlk2bk1k34kb"; var CONFIG = { endpoints: { api: "apicustomdomain.quickblox.com", // set custom API endpoint chat: "chatcustomdomain.quickblox.com", // set custom Chat endpoint }, chatProtocol: { active: 2, // set 1 to use BOSH, set 2 to use WebSockets (default) }, // * set { mode: 1 } or true to output to console, // * set { mode: 2, file: 'log.txt' } to output to file, // * set "false" to not output debug: { mode: 1 }, }; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY, CONFIG); ``` Set the `endpoints` field of the `CONFIG` object: | Field | Description | | ----- | ------------- | | api | API endpoint. | | chat | Chat endpoint | <Note> [Contact our sales team](https://quickblox.com/enterprise/) to get **API endpoint** and **chat endpoint**. </Note> ## Enable logging Logging functionality allows you to keep track of all events and activities while running your app. As a result, you can monitor the operation of the SDK and improve the debug efficiency. Set the debug mode in the `CONFIG` object and then call the `init()` method. ```JavaScript JavaScript theme={null} var APPLICATION_ID = 41; var AUTH_KEY = "lkjdueksu7392kj"; var AUTH_SECRET = "iiohfdija792hj"; var ACCOUNT_KEY = "sdjfnksnlk2bk1k34kb"; var CONFIG = { // other settings // * set { mode: 1 } or true to output to console, // * set { mode: 2, file: 'log.txt' } to output to file, // * set "false" to not output debug: { mode: 1 } }; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY, CONFIG); ``` There are 3 debug modes: | Debug modes | Description | | ----------- | ------------------------------------------------------------------------------------------------------------------------ | | 0 | Turns off logging output. | | 1 | Enables logging to the browser console with **console.log()** function (**default** value). | | 2 | Enables logging to a file. Set the file in the file field. Works **only** on Node.js, in case of server implementations. | ## Message carbons Message carbons functionality allows for multi-device support. Thus, all user messages get copied to all their devices so they could keep up with the current state of the conversation. For example, a User A has phone running conversations and desktop running conversations. User B has desktop running conversations. When User B sends a message to User A, the message shows on both the desktop and phone of User A. <Note> Message carbons are always enabled. </Note> ## Stream management Stream management has two important features **Stanza Acknowledgements** and **Stream Resumption**: * **Stanza Acknowledgements** is the ability to know if a stanza or series of stanzas has been received by one's peer. In other words, a reply is requested on every sent message. If the reply is received, the message is considered as delivered. * **Stream Resumption** is the ability to quickly resume a stream that has been terminated. Once a connection is re-established, Stream Resumption is executed. By matching the sequence numbers assigned to each Stanza Acknowledgement a server and client can verify which messages are missing and request to resend missing messages. ```JavaScript JavaScript theme={null} var CONFIG = { streamManagement: { enable: true } }; ``` <Note> **By default**, stream management functionality is disabled. Make sure to enable it using the `CONFIG` parameter when calling `init()` method. Review [Initialize QuickBlox SDK](#section-initialize-quick-blox-sdk) section for more details. </Note> ## Custom ICE servers You can customize a list of ICE servers. **By default**, WebRTC module will use internal ICE servers that are usually enough, but you can always set your own. WebRTC engine will choose the TURN relay with the lowest round-trip time. Thus, setting multiple TURN servers allows your application to scale-up in terms of bandwidth and number of users. ```JavaScript JavaScript theme={null} var CONFIG = { webrtc: { iceServers: [ { urls: "stun:stun.randomserver.example", username: "stun_login", credential: "stun_password" }, { urls: "turn:turn.randomserver.example", username: "turn_login", credential: "turn_password" } ] } }; ``` # Users Source: https://docs.quickblox.com/sdks/js-users Learn how to manage your users with QuickBlox. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. ## Create user It's recommended to manage user creation at your backend for production. To learn more you can refer to [QuickBlox API documentation](https://docs.quickblox.com/reference/create-user). For POCs/MVPs or during development you may want to create users on the fly, you can use `create()` method. Create a user using the code snippet below. Only login (or email) and password are required. Other fields are optional. ```JavaScript JavaScript theme={null} var params = { login: login, password: "someSecret", full_name: "QuickBlox Test" }; QB.users.create(params, function(error, result) { if (error) { done.fail("Create user error: " + JSON.stringify(error)); } else { } }); ``` <Warning> **Security & Privacy** It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. Email, full name, facebookId and phone number are PII, [configure session permissions](/docs/application#set-session-permissions) according to your privacy requirements. </Warning> ## Retrieve users Get a list of users using the `listUsers()` method. The code snippet below shows how to get a list of users created between the two given dates, sorted in ascending order, with `50` users on the page. ```JavaScript JavaScript theme={null} var params = { filter: { field: "created_at", param: 'between', value: '2021-01-01, 2021-05-06' }, order: { field: 'created_at', sort: 'asc' }, page: 1, per_page: 50 }; QB.users.listUsers(params, function(error, result){ }); ``` If you want to retrieve only users updated after some specific date time, you can use operators. This is useful if you cache users somehow and do not want to obtain the whole list of your users on every app start. Thus, you can use [search](/sdks/js-users#search-operators) and [sort](/sdks/js-users#sort-operators) operators to list users on the page so that it is easier to see specific users. If you want to get a paginated list of users from the server, you can set the following fields of the `filter`: | Field | Required | Description | | -------- | -------- | -------------------------------------------- | | page | no | Number of pages with results to be returned. | | per page | no | Number of records to return in one page. | ### Search operators You can use search operators to get more specific search results. The request below will return users by IDs. ```JavaScript JavaScript theme={null} var params = {filter: { field: 'id', param: 'in', value: [22,33] }}; QB.users.listUsers(params, function(error, result) { }); ``` Here are the search operators you can use to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ | | lt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less Than** operator. | | gt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** operator. | | gte | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** or **Equal** to operator. | | le | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less or Equal to** operator | | eq | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Equal** to operator. | | ne | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Not Equal** to operator. | | between | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Contained between values** operator. | | in | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **IN** array operator. | ### Sort operators You can use sort operators to order the search results. The request below will return users by the `created_at` field sorted in descending order. ```JavaScript JavaScript theme={null} var params = { filter: { field: "created_at", param: 'between', value: '2021-01-01, 2021-05-06' }, order: { field: 'created_at', sort: 'desc' }, }; QB.users.listUsers(params, function(error, result){ }); ``` Here are the sort operators you can use to order the search results. | Sort operator | Applicable to types | Applicable to fields | Description | | ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | | asc | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Search results will be sorted in ascending order by the specified field. | | desc | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Search results will be sorted in descending order by the specified field. | ## Retrieve users by ID To get a list of users by ID for a current account, use the following code snippet. ```JavaScript JavaScript theme={null} var searchParams = {filter: { field: 'id', param: 'in', value: [22,33] }}; QB.users.listUsers(searchParams, function(error, result) { }); ``` ## Retrieve user by login To get a list of users by login for a current account, use the following code snippet. ```JavaScript JavaScript theme={null} var searchParams = {login: "marvin18"}; QB.users.get(searchParams, function(error, user) { }); ``` ## Retrieve user by email To get a list of users by email for a current account, use the following code snippet. ```JavaScript JavaScript theme={null} var searchParams = {email: "[[email protected]](/cdn-cgi/l/email-protection)"}; QB.users.get(searchParams, function(error, user) { }); ``` ## Retrieve users by full name To get a list of users by full name for a current account, use the following code snippet. Search **requires** min 3 characters. ```JavaScript JavaScript theme={null} var searchParams = { full_name: "Marvin Samuel", //substring search order: { field: 'updated_at', sort: 'desc' }, page: 1, per_page: 50 }; QB.users.get(searchParams, function(error, result) { }); ``` ## Retrieve users by phone number To get a list of users by phone number for a current account, use the following code snippet. ```JavaScript JavaScript theme={null} var searchParams = {phone: "44678162873"}; QB.users.get(searchParams, function(error, result) { }); ``` ## Retrieve user by external user ID If you have your own database with users (we call these databases as "external databases"), you can use External User ID (`ExternalID` field) in `QBUser` model to link users from QuickBlox with users from your external database. ```JavaScript JavaScript theme={null} var searchParams = {external_user_id: "675373912"}; QB.users.get(searchParams, function(error, user) { }); ``` ## Retrieve users by tags To get a list of users by tags for a current account, use the following code snippet. ```JavaScript JavaScript theme={null} var searchParams = {tags: ["apple"]}; QB.users.get(searchParams, function(error, result) { }); ``` ## Delete user A user can delete himself from the platform. ```JavaScript JavaScript theme={null} var userId = 1; QB.users.delete(userId, function(error, result) { }); ``` ## Reset user password It's possible to reset a password via email. ```JavaScript JavaScript theme={null} QB.users.resetPassword("[[email protected]](/cdn-cgi/l/email-protection)", function(error) { }); ``` <Note> Make sure to enable the email confirmation. This functionality allows application users to confirm their emails. If a user doesn't confirm the email, the emails won't be sent to this user. As a result, a password reset functionality won't work. To enable the email confirmation, proceed as follows: 1. Go to the **Dashboard => *YOUR\_APP* => Users => Settings => User registration confirmation** and check the box. 2. Click the **Save** button. </Note> <Warning> A password reset functionality is available for the Enterprise plan. [Contact the sales team](https://quickblox.com/enterprise/#get) for more details. </Warning> ## Update user Update a user profile by calling the `updateUser()` method. If you want to change your password, you need to provide 2 parameters: `password` and `old_password`. As a result, the updated user entity will be returned. You can update any other field of the user using the `update()` method. Thus, the snippet below shows how to update a `tag_list` and `custom_data` fields. ```JavaScript JavaScript theme={null} var userId = 1, custom_data = JSON.stringify({ name: 'John', age: 31, city: 'New York' }), updatedUserProfile = { tag_list: 'tagOne,tagTwo', custom_data }; QB.users.update(userId, updatedUserProfile, function(error, user) {}); ``` | Field | Required | Description | | ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | tag\_list | no | User tags. An array of Strings. There are no spaces in the tag format. For example, the "tagOne" format is correct while the "tag one" format is incorrect. The maximum number of tags is 10. If more than 10 tags are provided, an error is returned: tag list should contain maximum 10 tags. | | custom\_data | no | User custom data. Should be a String. You can convert any data types to String, or example, JSON, XML, etc. | ## Set user avatar To set a user avatar, just upload a file to the QuickBlox cloud storage and connect it to the user. To upload the file to the QuickBlox cloud storage, call the `createAndUpload()` method. Now that the file is uploaded, get the ID of the uploaded file. To connect the file to the user, set the ID of the uploaded file to the `blob_id` field of the `user` and call the `update()` method. As a result, the user avatar gets updated. ```JavaScript JavaScript theme={null} // for example, a file from HTML form input field var inputFile = $("input[type=file]")[0].files[0]; var fileParams = { name: inputFile.name, file: inputFile, type: inputFile.type, size: inputFile.size, public: false, }; QB.content.createAndUpload(fileParams, function(error, result) { if (error) { } else { var userId = 1; var updatedUserProfile = { blob_id: result.id }; QB.users.update(userId, updatedUserProfile, function(error, user) {}); } }); ``` Pass the following arguments to the `update()` method. | Argument | Required | Description | | ------------------ | -------- | ---------------------------------------------------------------------------- | | userId | yes | User ID. | | updatedUserProfile | yes | Specifies the updatedUserProfile fields that should be set. | | function | yes | Specifies a callback function that accepts an error and updated user entity. | ## Get user avatar Now, other users can get access to your avatar by using the code snippet below. As a result, you will receive a private URL in the response. See [this section](/sdks/js-content#get-file-url) to learn more about file URLs. ```JavaScript JavaScript theme={null} var fileId = someUser.blob_id; QB.content.getInfo(fileId, function(error, result) { if (error) { done.fail("Get file information by ID error: " + JSON.stringify(error)); } else { var fileUID = result.blob.uid; var fileUrl = QB.content.privateUrl(fileUID); var imageHTML = "<img src='" + fileUrl + "' alt='photo'/>"; } }); ``` # Basic Source: https://docs.quickblox.com/sdks/js-video-calling Learn how to add peer-to-peer video calls to your app. QuickBlox Video Calling API is built on top of [WebRTC](https://webrtc.org/). It allows adding real-time video communication features into your app similar to Skype using API easily. The communication is happening between peers representing camera devices. There are two **peer types**: * **Local peer** is a device running the app right now. * **Remote peer** is an opponent device. Establishing real-time video communication between two peers involves 3 phases: 1. **Signaling**. At this phase, the peers’ local IPs and ports where they can be reached (ICE candidates) are exchanged as well their media capabilities and call session control messages. 2. **Discovery**. At this phase, the public IPs and ports at which endpoints can be reached are discovered by STUN/TURN server. 3. **Establishing a connection**. At this phase, the data are sent directly to each party of the communication process. <Warning> In order to start using Video Calling Module, you need to **connect to QuickBlox Chat first**. The signaling in the QuickBox WebRTC module is implemented over the XMPP protocol using QuickBlox Chat Module. It acts as a signaling transport for Video Calling API. </Warning> <Note> Please use this WebRTC Video Calling to make the Group Calls with **4 or fewer users**. Because of [Mesh architecture](https://webrtcglossary.com/mesh/) we use for multi-point where every participant sends and receives its media to all other participants, the current solution supports group calls with up to 4 people. </Note> Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/js-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/js-authentication) page to learn how to do it. 4. Connect to the Chat server to provide a signaling mechanism for Video Calling API. Follow our [Chat](/sdks/js-chat) page to learn about chat connection settings and configuration. ## Initiate a call To initiate a call, proceed with the steps below. ### Create session First, you need to create a call session. This is done by using the `createNewSession()` method. Pass opponent ID/IDs and a call type (video or audio) to it. The session object can be received either as a result of the `createNewSession()` method or in each event listener callback. Learn more about the event listener in [this section](/sdks/js-video-calling#event-listener). ```JavaScript JavaScript theme={null} var calleesIds = [56, 76, 34]; // Users' ids var sessionType = QB.webrtc.CallType.VIDEO; // AUDIO is also possible var additionalOptions = {}; var session = QB.webrtc.createNewSession(calleesIds, sessionType, null, additionalOptions); ``` | Argument | Required | Description | | ----------- | -------- | -------------------------- | | calleesIds | yes | IDs of opponents. | | sessionType | yes | Call type: AUDIO or VIDEO. | ### Access local media stream Once a call session is created, you need to get access to the user's devices (webcam/microphone) in order to make a call. ```JavaScript JavaScript theme={null} var mediaParams = { audio: true, video: true, options: { muted: true, mirror: true, }, elemId: "localVideoElem", }; session.getUserMedia(mediaParams, function (error, stream) { if (error) { } else { //run call function here } }); ``` | Parameters | Required | Description | | ----------- | -------- | ------------------------------------------------------------------------------------- | | mediaParams | yes | Media stream constrains. | | function() | yes | Specifies the callback function which receives a local media stream once it is ready. | The `mediaParams` object has the following fields: | Fields | Required | Description | | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | audio | yes | Enable audio. Boolean parameter. true is enabled, false is disabled. | | video | yes | Enable video. Boolean parameter. true is enabled, false is disabled. | | options | no | Specifies two media options:- mute. Mute audio. true is muted, false is unmuted.- mirror. Enable the mirror. true is enabled, false is disabled. | | elemId | no | ID attribute of the video element to attach a local video stream to. | This method lets the browser ask the user for permission to use devices. You should allow this dialog to access the stream. Otherwise, the browser cannot obtain access and will throw an error for `getUserMedia` callback function. For more information about possible audio/video constraints, here is a good code sample from WebRTC team on [how to work with getUserMedia constraints](https://webrtc.github.io/samples/src/content/getusermedia/resolution/). ### Attach local media stream You also need to attach your local media stream to HTML video element. The method below attaches the Media Stream Object to your DOM element and autoplays it. ```JavaScript JavaScript theme={null} session.attachMediaStream('myVideoElementId', stream); ``` <Note> The same method is used for attaching the **remote** video stream that is received from your opponents. </Note> ### Make a call Now that you have attached the local media stream, you can initiate a call by calling the `call()` method. ```JavaScript JavaScript theme={null} var extension = {}; session.call(extension, function(error) { }); ``` | Argument | Required | Description | | --------- | -------- | ------------------------------------------------------------------------------------------------------ | | extension | yes | Custom user data. Specify any key-value pairs. In each pair, the key and value are both string values. | | fuction() | yes | Specifies the callback function. The error is received in case of unsuccessful callback. | As a result, your opponent will receive an `onCallListener()` callback specifying that a new call session has been received. ```JavaScript JavaScript theme={null} QB.webrtc.onCallListener = function(session, extension) { // if you are going to take a call session.getUserMedia(mediaParams, function (error, stream) { if (error) { } else { //run accept function here } }); }; ``` <Note> Your opponent receives an incoming call through either the established connection with the Chat server or a VoIP Push Notifications when the app is in the background. </Note> ## Accept a call To accept a call request, call `accept()` method. ```JavaScript JavaScript theme={null} var extension = {}; session.accept(extension); ``` After this, your opponents will get a confirmation in the following callback. ```JavaScript JavaScript theme={null} QB.webrtc.onAcceptCallListener = function(session, userId, extension) { //here you can stop the ringtone }; ``` Also, both the caller and opponents will get a special callback with the remote stream. ```JavaScript JavaScript theme={null} QB.webrtc.onRemoteStreamListener = function(session, userID, remoteStream) { // attach the remote stream to DOM element session.attachMediaStream("remoteOpponentVideoElementId", remoteStream); }; ``` From this point, you and your opponents can see each other. ## Reject a call To reject a call request, use `reject()` method. ```JavaScript JavaScript theme={null} var extension = {}; session.reject(extension); ``` After this, the caller will get a confirmation in the following callback. ```JavaScript JavaScript theme={null} QB.webrtc.onRejectCallListener = function(session, userId, extension) { }; ``` ## End a call To end a call, use `stop()` method and pass `sessionId` parameter to tell SDK which call session to end. ```JavaScript JavaScript theme={null} var extension = {}; session.stop(extension); ``` After this, the opponents will get a confirmation in the following callback. ```JavaScript JavaScript theme={null} QB.webrtc.onStopCallListener = function(session, userId, extension) { }; ``` ## Call changes Sometimes, you make changes during an active call (for example, applying some CSS filter to a video element) that needs to be fed to the opponents and updated. To allow for this, you can use `update()` method to send instructions to the opponents on how to update the UI. ```JavaScript JavaScript theme={null} var extension = { userInfo: { videoFilter: "sepica" } }; session.update(extension); ``` Your opponents will receive the following callback. ```JavaScript JavaScript theme={null} QB.webrtc.onUpdateCallListener = function(session, userId, extension) { }; ``` ## Event listener To process events such as incoming call, call reject, hang up, etc. you need to set up the event listener. The event listener processes various events that happen with the call session or peer connection in your app. Using the callbacks provided by the event delegate, you can implement and execute the event-related processing code. For example, the `onAcceptCallListener` callback is received when your call has been accepted by the user. This callback receives information about the call session, user ID who accepted the call, and additional key-value data about the user. QuickBlox JavaScript SDK persistently interacts with the server via XMPP connection that works as a signaling transport for establishing a call between two or more peers. It receives the callbacks of the asynchronous events which happen with the call and peer connection. This allows you to track these events and build your own video calling features around them. To track call session and peer connection events, you should use the event listener. The supported event callbacks for a call session and peer connection are listed in the table below. | Method | Invoked when | | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | onCallListener | A new call session has been received. | | onAcceptCallListener | A call has been accepted. | | onRejectCallListener | A call has been rejected. | | onStopCallListener | An accepted call has been ended by the peer by pressing the hang-up button. | | onUserNotAnswerListener | A user did not respond to your call within the dedicated time interval. | | onSessionCloseListener | A call session has been closed. | | onCallStatsReport | An updated stats report, which is called by timeout, has been received for the user ID. | | onSessionConnectionStateChangedListener | A peer connection state has been changed. View [Peer connection states](#peer-connection-states) section for more details. | | onUpdateCallListener | Called when updates to the active call are made. | | onInvalidEventsListener | Called when some operation cannot be performed (for example, `accept()` method is called on the closed call session). | | onRemoteStreamListener | A user received a stream from the opponent. | The following code lists all supported event callbacks. ```JavaScript JavaScript theme={null} QB.webrtc.onCallListener = function(session, extension) { } QB.webrtc.onAcceptCallListener = function(session, userId, extension) { } QB.webrtc.onRejectCallListener = function(session, userId, extension) { } QB.webrtc.onStopCallListener = function(session, userId, extension) { } QB.webrtc.onUpdateCallListener = function(session, userId, extension) { } QB.webrtc.onInvalidEventsListener = function(eventName, session, userId, userInfo) {} QB.webrtc.onUserNotAnswerListener = function(session, userId) { } QB.webrtc.onRemoteStreamListener = function(session, userId, stream) { } QB.webrtc.onSessionConnectionStateChangedListener = function(session, userId, connectionState) { } QB.webrtc.onSessionCloseListener = function(session) {} QB.webrtc.onCallStatsReport = function(session, userId, stats, error) { } ``` Go to the [Resources](#resources) section to see a sequence diagram for a regular call workflow. ### Call session states The following table lists all supported call session states. | State | Description | | -------- | ------------------------------------------------------------------------------------------------ | | NEW | A new call session has such a state. | | ACTIVE | A call session gets this state when you initiated or accepted the incoming call. | | HUNGUP | A call session gets this state when you hang up, before closing the call session. | | REJECTED | A call session gets this state when you reject an incoming call before closing the call session. | | CLOSED | A call session is closed. | ### Call reconnection states <Note> Since version 2.15.0 Quickblox-WebRTC \`\`\`JavaScript JavaScript SDK supports reconnection functional. </Note> ### Manage reconnection You could add onReconnectListener to your client code to define session states and manage reconnection states. ```JavaScript JavaScript theme={null} QB.webrtc.onReconnectListener = function (session, userId, state) { console.group('onReconnectListener'); console.log('Session: ', session); console.log('User Id: ', userId); console.log('Reconnection state: ', state); console.groupEnd(); }; ``` <Warning> Doesn’t invoke in conference. </Warning> The following table lists all supported call reconnection states: | State | Description | | ------------------------------ | -------------------------------------------------------------------------------- | | ReconnectionState.RECONNECTING | A connection with opponent in reconnection progress after ICE connection failed. | | ReconnectionState.RECONNECTED | A connection with opponent was successfully established. | | ReconnectionState.FAILED | A connection with opponent isn’t restored by disconnect time interval. | You can also increase disconnect timeout. By default the time is set to 30 seconds. Minimal time is 10 seconds. It’s time while users can have availability to reconnect. You could set the disconnectTimeInterval in the `CONFIG` object and then call the `init()` method. ```JavaScript JavaScript theme={null} var APPLICATION_ID = 41; var AUTH_KEY = "lkjdueksu7392kj"; var AUTH_SECRET = "iiohfdija792hj"; var ACCOUNT_KEY = "sdjfnksnlk2bk1k34kb"; var CONFIG = { // other settings webrtc: { disconnectTimeInterval: 30, } // other settings }; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY, CONFIG); ``` ### Peer connection states The following table lists all supported states of peer connection: | State | Description | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | QB.webrtc.SessionConnectionState.CONNECTING | The ICE agent has been given one or more remote candidates and is checking pairs of local and remote candidates against one another to try to find a compatible match, but has not yet found a pair which will allow the peer connection to be made. | | QB.webrtc.SessionConnectionState.CONNECTED | A usable pairing of local and remote candidates has been found for all components of the connection, and the connection has been established. | | QB.webrtc.SessionConnectionState.CLOSED | A peer connection was closed. But the call session can still be open because there can several peer connections in a single call session.The ICE agent for this RTCPeerConnection has shut down and is no longer handling requests. | | QB.webrtc.SessionConnectionState.FAILED | One or more of the ICE transports on the connection is in the failed state. This can occur in different circumstances, for example, bad network, etc. | | QB.webrtc.SessionConnectionState.COMPLETED | The ICE agent has finished gathering candidates, has checked all pairs against one another, and has found a connection for all components. | ## Resources A regular call workflow. <Frame> <img alt="JS_SDK_initiate_accept_hang_up.png" /> </Frame> # Advanced Source: https://docs.quickblox.com/sdks/js-video-calling-advanced Learn how to mute audio, disable video, switch camera, share your screen, configure media settings, etc. ## Mute audio Mute the audio by calling the `mute()` method. Using this method, we can tell SDK to send/not send audio data either from a local or remote peer. Unmute the audio by calling the `unmute()` method. ```JavaScript JavaScript theme={null} session.mute('audio'); session.unmute('audio'); ``` ## Disable video Disable the video by calling the `mute()` method. Using this method, we can tell SDK to send/not send video data either from a local or remote peer. Enable the video by calling the `unmute()` method. ```JavaScript JavaScript theme={null} session.mute('video'); session.unmute('video'); ``` ## Switch camera You can switch the video camera during a call by calling the `getMediaDevices()` method. ```JavaScript JavaScript theme={null} QB.webrtc.getMediaDevices("videoinput").then(function(devices) { if (devices.length) { // here is a list of all available cameras for (var i = 0; i < devices.length; i++) { var deviceInfo = devices[i]; var deviceId = deviceInfo.deviceId; var deviceLabel = deviceInfo.label; } } }); ``` Then you can choose some `deviceId` and switch the video stream to the exact device. ```JavaScript JavaScript theme={null} var constraints = { audio: audioDeviceId || undefined, video: { exact: deviceId } }; session.switchMediaTracks(constraints, function(error, stream) { }); ``` ## Screen sharing Use the screen sharing functionality to allow your users to share their screen. To enable screen sharing, you need to have an active call session that becomes active after the `call()` method is called, and the `onCallListener()` callback is received by the remote user. Learn more details from the [Initate call](/sdks/js-video-calling#initiate-a-call) section. As soon as you have a running call session use methods below: * Use the `runScreenSharing()` to enable the screen sharing. As a result, the camera video track in the local stream will be switched to the screen-sharing video track. * Use the `stopScreenSharing()` to interrupt the screen sharing. As a result, the screen-sharing video track will be switched to the camera video track in the local stream. ```JavaScript JavaScript theme={null} QB.webrtc.onCallListener = function onCallListener(session, extension) { // @type {qbWebRTCSession} app.currentSession = session; }; var runScreenSharing = function () { navigator.mediaDevices .getDisplayMedia({ video: true, }) .then((stream) => { var videoTrack = stream.getVideoTracks()[0]; videoTrack.onended = stopScreenSharing; switchMediaTrack(videoTrack); }); }, stopScreenSharing = function () { navigator.mediaDevices .getUserMedia({ video: true, }) .then((stream) => { switchMediaTrack(stream.getVideoTracks()[0]); }); }, switchMediaTrack = function (track) { app.currentSession.localStream.getVideoTracks()[0].stop(); var stream = app.currentSession.localStream.clone(); stream.removeTrack(stream.getVideoTracks()[0]); stream.addTrack(track); app.currentSession.localStream.getAudioTracks()[0].stop(); app.currentSession._replaceTracks(stream); app.currentSession.localStream = stream; return true; }; runScreenSharing(); stopScreenSharing(); ``` ## Mirror local video A mirror functionality allows to flip the video horizontally. Enable the mirroring when getting access to the local media steam using the `getUserMedia()` method. Set the `mirror` as to `true` to enable mirroring. ```JavaScript JavaScript theme={null} var mediaParams = { audio: true, video: true, options: { muted: true, mirror: true, }, elemId: "localVideo", }; session.getUserMedia(mediaParams, function (error, stream) { if (error) { } else { } }); ``` ## WebRTC stats reporting You are able to receive the information report about the current connection, audio, video tracks, and other useful information. To set a receiving time interval, use the code snippet below. Review our [Setup](/sdks/js-setup) guide to learn more about `CONFIG` configuration. ```JavaScript JavaScript theme={null} var CONFIG = { webrtc: { //... statsReportTimeInterval: true, }, //... }; ``` Then you should use the `onCallStatsReport` callback to listen to the stats report. ```JavaScript JavaScript theme={null} QB.webrtc.onCallStatsReport = function onCallStatsReport(session, userId, stats, error) { ``` ## General settings You can change different settings for your calls using `CONFIG` object. Review our [Setup](/sdks/js-setup) guide to learn more about `CONFIG` configuration. ### Custom ICE servers You can customize a list of ICE servers. **By default**, WebRTC module will use internal ICE servers that are usually enough, but you can always set your own. WebRTC engine will choose the TURN relay with the lowest round-trip time. Thus, setting multiple TURN servers allows your application to scale-up in terms of bandwidth and number of users. Review our [Setup](/sdks/js-setup#section--custom-ice-servers) guide. ### Video calling settings Set video calling settings using the `webrtc` fields. ```JavaScript JavaScript theme={null} var CONFIG = { webrtc: { answerTimeInterval: 60, autoReject: true, incomingLimit: 1, dialingTimeInterval: 5, disconnectTimeInterval: 30, statsReportTimeInterval: false, //... } }; ``` | Parameters | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | answerTimeInterval | Maximum answer timefor the QB.webrtc.onUserNotAnswerListener callback to be fired. The answer time interval shows how much time an opponent has to answer your call. | | autoReject | If there is at least one active (recurring) call session and the autoReject is true, the call gets rejected. | | incomingLimit | If the number of active (recurring) call sessionsis more than it is defined by incomingLimit, the call gets rejected. | | dialingTimeInterval | The interval between call requests produced by the session.call() method. Dialing time interval indicates how often to notify your opponents about your call. | | disconnectTimeInterval | If an opponent has lost the connection then, after this time, the caller will know about it via the QB.webrtc.onSessionConnectionStateChangedListener callback. | | statsReportTimeInterval | Allows access to the statistical information about peer connection state (connected, failed, disconnected, etc). Set the number of seconds for the statistical information to be received. | ## Media settings Learn how to configure media settings following the sections below. ### Bitrate A bitrate is a video stream encoding parameter. Set the bitrate and pass it within the `createNewSession()` method. Default: **0**. ```JavaScript JavaScript theme={null} QB.webrtc.createNewSession(Object.keys(app.callees), isAudio ? QB.webrtc.CallType.AUDIO : QB.webrtc.CallType.VIDEO, null, {'bandwidth': bandwidth}); ``` <Note> If the parameter is set to more than `0`, the quality of the video is degraded. </Note> ### Camera resolution A camera resolution is a video stream encoding parameter. It's possible to set custom video resolution using `getUserMedia` constraints. See [getUserMedia](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#Syntax) for more examples. ```JavaScript JavaScript theme={null} var params = { audio: true, video: { min: 320, ideal: 720, max: 1920 } }; var callback = function(error, stream) { /* ... */ }; session.getUserMedia(params, callback); ``` # Video Conference Source: https://docs.quickblox.com/sdks/js-video-conference Learn how to add video conference calls to your app. <Warning> This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). </Warning> QuickBlox provides a Multiparty Video Conferencing solution allowing to set up a video conference between 10-12 people. It is built on top of [WebRTC SFU](https://webrtcglossary.com/sfu/) technologies. Features supported: \- Video/Audio Conference with 10-12 people. \- Join/Rejoin video room functionality (like Skype). \- Mute/Unmute audio/video stream (own and opponents). \- Display bitrate. \- Switch video input device (camera). ## Dependencies for browser For the library to work, you need to include a [webrtc-adapter](https://github.com/webrtchacks/adapter) in your html before `quickblox-multiparty-video-conferencing-client-0.8.6.min.js`. ```JavaScript JavaScript theme={null} <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> <script src="quickblox-multiparty-video-conferencing-client-0.8.6.min.js"></script> ``` ## Initialize In order to start working with Multiparty Video Conferencing API, you need to initialize a client. ```JavaScript JavaScript theme={null} const config = { server: "wss://...", debug: true, // optional iceServers: [], // optional }; const client = new QBVideoConferencingClient(config); ``` Set the following fields using the `config` object: | Field | Required | Description | | ---------- | -------- | ---------------------------------------------------- | | server | yes | A conference server endpoint or array of endpoints. | | debug | no | Enables logging. true is enabled. false is disabled. | | iceServers | no | A list of ICE servers. | <Note> To request your server endpoint, contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). </Note> ## Create session Once the library has been initialized, you can start creating a session. Typically, each browser tab will need a single session with the server. Once the session is created, you can interact with a Video Conferencing API. ```JavaScript JavaScript theme={null} client .createSession() .then(() => { // session created }) .catch((error) => { // some error occurred }); ``` ## Attach video conferencing plugin Video conferencing allows you to exploit features of the plugin to manipulate the media sent and/or received by peer connection on your web page. This method will create a plugin handle that you can subscribe to and handle events. You also have to attach the plugin to each remote user. ```JavaScript JavaScript theme={null} const isRemote = remoteUser ? true : false; const userId = remoteUser ? remoteUser.id : null; client .attachVideoConferencingPlugin(isRemote, userId) .then((plugin) => { const eventHandler = console.log; // use your own event handler(s) Object.keys(plugin.events).forEach((key) => plugin.on(plugin.events[key], eventHandler) ); }) .catch((error) => { // some error occurred }); ``` <Note> To attach the plugin to a local user plugin, set `userId` to `null` and `isRemote` to `false`. </Note> The following code lists the supported events of the video conferencing plugin as well as shows how to add the listener. ```JavaScript JavaScript theme={null} function consentDialog(on) {} function mediaState(media, receiving) {} function webrtcState(on, reason) {} function slowLink(uplink, lost) {} function iceState(state) {} function detached() {} function cleanup() {} plugin.addListener(plugin.events.CONSENT_DIALOG, consentDialog); plugin.addListener(plugin.events.MEDIA_STATE, mediaState); plugin.addListener(plugin.events.WEBRTC_STATE, webrtcState); plugin.addListener(plugin.events.SLOW_LINK, slowLink); plugin.addListener(plugin.events.ICE_STATE, iceState); plugin.addListener(plugin.events.DETACHED, detached); plugin.addListener(plugin.events.CLEANUP, cleanup); ``` | Event | Description | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | plugin.events.CONSENT\_DIALOG | This event is triggered just before the getUserMedia() method is called and after it is completed. This means it can be used to modify the UI accordingly. For example, to prompt the user to accept the device access requests. | | plugin.events.MEDIA\_STATE | This event is triggered when the media server starts or stops receiving your media. For example, a mediaState with type=audio and on=true means that the media server started receiving your audio stream or started getting it again after a pause of more than a second. A mediaState with type=video and on=false means that the media server hasn't received any video from you in the last second after a start. This event is useful to figure out when the media server actually started handling your media, or to detect problems on the media path (for example,, media never started, or stopped at some point of time time). | | plugin.events.WEBRTC\_STATE | This event is triggered with a true value when the PeerConnection associated with a handle becomes active (so ICE, DTLS and everything else succeeded) from the media server perspective. A false value is triggered when the PeerConnection goes down instead. This event is useful to figure out when WebRTC is actually up and running between you and the media server. In case of the false value, a reason of the String type may be present as an optional parameter. | | plugin.events.SLOW\_LINK | This event is triggered when the media server reports trouble associated with either sending or receiving media on the specified PeerConnection. Typically, it is triggered as a consequence of too many NACKs received from/sent to the user in the last second. For example, a slowLink with uplink=true means you notified several missing packets from the media server, while uplink=false means the media server is not receiving all your packets. This event is useful to figure out when there are problems on the media path (for example,, excessive loss), in order to react accordingly (for example,, decrease the bitrate if most of our packets are getting lost). | | plugin.events.ICE\_STATE | This event is triggered when the ICE state for the PeerConnection associated with the handle changes. Thus, the argument of the callback is a new state of the String type (for example,, "connected" or "failed"). | | plugin.events.DETACHED | The plugin handle has been detached by the plugin itself, and so should not be used anymore. | | plugin.events.CLEANUP | The WebRTC peer connection with the plugin was closed. | ## Join video room To jump into a video conference room, join it using the `join()` method. ```JavaScript JavaScript theme={null} const joinParams = { roomId: "room_123", userId: 12345, display: "John Doe", onlyAudio: false, role: "publisher", video: "stdres", }; client .join(joinParams) .then(() => { // joined successfully }) .catch((error) => { // handle error }); ``` Set the following fields using the `joinParams` object: | Field | Required | Description | | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------- | | roomId | yes | A room ID, should be of String type. | | userId | yes | A user ID, should be of Number type. | | display | no | A name to be shown to other participants, should be of String type. | | onlyAudio | no | Specifies whether to use the audio only. A Boolean parameter. Default: `false`. | | role | no | A user role. Possible values are `subscriber` or `publisher`. Default: `publisher`. | | video | no | A video property. Default: `lowres`. See [this section](/sdks/js-video-conference#camera-resolution) for more information. | To check the current video room you are joined to, use the following property of the `QBVideoConferencingClient`. ```JavaScript JavaScript theme={null} client.currentRoomId ``` The `QBVideoConferencingClient` has the following properties: | Property | Description | | ------------- | ---------------------------------------------------------------------------------- | | connected | Returns a boolean indicating whether the client is connected to the server or not. | | pluginId | Returns a unique plugin ID for the current user. | | sessionId | Returns a current session ID indicating if there is a session. | | currentRoomId | Returns a current room ID. | ## List online participants To list online users in a video conference room, use the `listOnlineParticipants()` method. ```JavaScript JavaScript theme={null} client .listOnlineParticipants(roomId) .then((participants) => { // handle as necessary }) .catch((error) => { // handle error }); ``` ## Events There is a number of events you can subscribe to. You can inspect the property `events` to check the list of supported events. To assign an event handler, you can use the same API as for [NodeJS EventEmitter](https://nodejs.org/dist/latest-v12.x/docs/api/events.html#events%5Fclass%5Feventemitter). ```JavaScript JavaScript theme={null} client.on(client.events.PARTICIPANT_JOINED, (userId, userDisplayName) => {}); client.on(client.events.PARTICIPANT_LEFT, (userId, userDisplayName) => {}); client.on(client.events.LOCAL_STREAM, (stream) => { const localVideo = document.querySelector("video#local"); QBVideoConferencingClient.attachMediaStream(localVideo, stream); }); client.on(client.events.REMOTE_STREAM, (stream, userId) => { const remoteVideo = document.querySelector("video#remote" + userId); QBVideoConferencingClient.attachMediaStream(remoteVideo, stream); }); client.on(client.events.SESSION_DESTROYED, () => {}); client.on(client.events.ERROR, (error) => { // handle error }); ``` | Event | Description | | --------------------------------- | ------------------------------------------------------------ | | client.events.PARTICIPANT\_JOINED | A participant has joined a conference room. | | client.events.PARTICIPANT\_LEFT | A participant has left a conference room. | | client.events.LOCAL\_STREAM | A local MediaStream is available and ready to be displayed. | | client.events.REMOTE\_STREAM | A remote MediaStream is available and ready to be displayed. | | client.events.SESSION\_DESTROYED | A conference session has been destroyed. | To remove all listeners the following code can be used. ```JavaScript JavaScript theme={null} Object.keys(client.events).forEach(key => client.removeAllListeners(client.events[key]) ); ``` ## Mute remote audio You also can mute/unmute a remote user's audio. ```JavaScript JavaScript theme={null} const muted = client.toggleRemoteAudioMute(userId); console.info("Now remote audio is muted=" + muted); ``` ## Disable local video You can enable/disable your own video. ```JavaScript JavaScript theme={null} const muted = client.toggleVideoMute(); console.info("Now video is muted=" + muted); ``` ## Disable remote video You also can enable/disable a remote user's video. ```JavaScript JavaScript theme={null} const muted = client.toggleRemoteVideoMute(userId); console.info("Now remote video is muted=" + muted); ``` ## Display bitrate There is a way to show a video bitrate of a remote user. It will start updating the `innerText` of the video element every second with the bitrate in 'kbps' for the remote user (if found by `userId`). Thus, the following string will be shown in the video element: **180 kbits/sec**. ```JavaScript JavaScript theme={null} const userId = 12344; const bitrateNode = document.getElementById("bitrate"); client.showBitrate(userId, bitrateNode); ``` In order to stop displaying the video bitrate of the remote user, use the code snippet below. It will clear `innerText` of the video element and stop updating the bitrate. ```JavaScript JavaScript theme={null} const userId = 12344; const bitrateNode = document.getElementById("bitrate"); client.hideBitrate(userId, bitrateNode); ``` ## List video input devices To get a list of all video input devices (cameras), use the following code snippet. ```JavaScript JavaScript theme={null} QBVideoConferencingClient .listVideoInputDevices() .then(videoInputDevices => { // handle as necessary }); ``` ## List audio input devices To get a list of all audio input devices (microphones), use the following code snippet. ```JavaScript JavaScript theme={null} QBVideoConferencingClient .listAudioInputDevices() .then(audioInputDevices => { // handle as necessary }); ``` ## Switch video input device In order to switch a video camera, you have to obtain a list of currently plugged video cameras with `listVideoInputDevices()` method. See [this section](/sdks/js-video-conference#list-video-input-devices) to learn how to list video input devices. Then, call the `switchVideoInput()`. ```JavaScript JavaScript theme={null} var deviceId = "..."; client .switchVideoInput(mediaDeviceId) .then(() => { // switched successfully }) .catch((error) => { // handle error }); ``` ## Switch audio input device In order to switch a microphone, you have to obtain a list of currently plugged microphones with the `listAudioInputDevices()` method. See [this section](/sdks/js-video-conference#list-audio-input-devices) to learn how to list audio input devices. Then, call the `switchAudioInput()`. ```JavaScript JavaScript theme={null} var deviceId = "..."; client .switchAudioInput(mediaDeviceId) .then(() => { // switched successfully }) .catch((error) => { // handle error }); ``` ## Screen sharing In order to start sharing your screen call the `toggleScreenSharing()` method. ```JavaScript JavaScript theme={null} client .toggleScreenSharing() .then(() => { // sharing your screen }) .catch((error) => { // some error occurred }); ``` To check if the screen sharing is enabled, call the `screenSharingEnabled()` method. ```JavaScript JavaScript theme={null} const enabled = client.screenSharingEnabled() ``` ## ICE Restart Initiate ICE restart for a peer. This is typically needed whenever something in your network changes. For example, you move from Wi-Fi to a mobile or a different Wi-Fi but want to keep the conversation going. In this case, an ICE restart needs to take place, as peers need to exchange new candidates they can be reached on. ```JavaScript JavaScript theme={null} const userId = remoteUser ? // restart ICE for remote user remoteUser.id : // or restart ICE for local (current) user null; client .iceRestart(userId) .then(() => { /* success */ }) .catch((error) => { /* some error occurred */ }); ``` ## Leave video room To leave the video room, use the `leave()` method. ```JavaScript JavaScript theme={null} client .leave() .then(() => { // left room successfully }) .catch((error) => { // handle error }); ``` ## Detach video conferencing plugin When the job is done, you should detach the video conferencing plugin. The `detachVideoConferencingPlugin()` method detaches the plugin and destroys the handle tearing down to the related peer connection if it exists. ```JavaScript JavaScript theme={null} client .detachVideoConferencingPlugin() .then(() => { // successfully detached }) .catch((error) => { // handle error }); ``` ## Destroy session To destroy a session, use the `destroySession()` method. ```JavaScript JavaScript theme={null} client .destroySession() .then(() => { // success }) .catch((error) => { // handle error }); ``` ## Camera resolution Set a camera resolution when joining a video room using the `video` property of the `joinParams` object. ```JavaScript JavaScript theme={null} const joinParams = { roomId: "room_123", userId: 12345, display: "John Doe", onlyAudio: false, role: "publisher", video: "stdres", }; client .join(joinParams) .then(() => { // joined successfully }) .catch((error) => { // handle error }); ``` The allowed values for the `video` property are the following: | Property | Description | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | video | A video property. Default: **lowres**. The possible values are:For a small resolutionlowres (240 × 320)lowres-16:9 (180 × 320)For a normal resolutionstdres (480 × 640)stdres-16:9 (360 × 640)For a High(HD) resolutionhires, hires-16:9, hdres (720 × 1280). High(HD) resolution is only 16:9.For a full HD resolutionfhdres (1080 × 1920). Full HD resolution is only 16:9.For a 4K resolution4kres (2160 × 3840). 4K resolution is only 16:9. | # Address Book Source: https://docs.quickblox.com/sdks/react-native-address-book Learn how to store and sync the phone contact list with QuickBlox. This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://help.quickblox.com/). # Authentication Source: https://docs.quickblox.com/sdks/react-native-authentication Learn how to authenticate your users with QuickBlox. Every user needs to authenticate with QuickBlox before using any QuickBlox functionality. When someone connects with an application using QuickBlox, the application needs to obtain a session token which provides temporary secure access to QuickBlox APIs. A session token is an opaque string that identifies a user and an application. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. ## Session token rights There are different types of session tokens to support different use cases. | Application session token | Description | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Application session token | This kind of access token is needed to read the app data. Has only READ access to resources. The expiration time after the last REST API request is 2 hours. | | User session token | The user token is the most commonly used type of token. This kind of access token is needed any time the app calls an API to read, modify or write a specific user's data on their behalf. Has READ/WRITE access to resources. The expiration time after the last REST API request is 2 hours. | ## Get session You can check whether you have a session or not. ```JavaScript JavaScript theme={null} QB.auth .getSession() .then(function (session) { // handle session }) .catch(function (e) { // something went wrong }); ``` ## Sign up user Before you log in the user, you must create the user on QuickBlox. Recommendations are below: 1. **For POCs/MVPs:** Create the user using the [QuickBlox Dashboard](https://admin.quickblox.com/) or in client app with application session token. 2. **For production apps:** Use the [QuickBlox Create User API](https://docs.quickblox.com/reference/create-user) with [API key](/docs/application#create-api-key) on your backend to create the user when your user signs up in your app. <Warning> **Security** It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. </Warning> You can create a user with application session token in client app by calling `create()` method. ```JavaScript JavaScript theme={null} const createUserParams = { fullName: 'Jack Sparrow', login: 'jack', password: 'jackpassword' }; QB.users .create(createUserParams) .then(function (user) { // user created successfully }) .catch(function (e) { // handle as necessary }); ``` ## Log in user QuickBlox provides four types of user authentication: login/email and password, social, phone number, and custom identity provider login. ### Login/email and password Standard login lets you log in a user just by login (or email) and password. Other fields are optional. Thus, the QuickBlox server requests a users database for a match. If there is a match, a user session is created. ```JavaScript JavaScript theme={null} const loginParams = { login: 'yourlogin', password: 'yourpassword' }; // or through email // const loginParams = {email: '[[email protected]](/cdn-cgi/l/email-protection)', password: 'garry5santos'}; QB.auth .login(loginParams) .then(function (info) { // signed in successfully, handle info as necessary // info.user - user information // info.session - current session }) .catch(function (e) { // handle error }); ``` ### Social Authenticate with QuickBlox using a facebook access token. ```JavaScript JavaScript theme={null} const facebookToken = 'EAAJ1nlH9EaQphl8WmFIJBhFn8XLMBu8UWAyxx1Uz0JLwZDZD'; QB.auth .loginWithFacebook(facebookToken) .then(function (info) { // signed in successfully, handle info as necessary // info.user - user information // info.session - current session }) .catch(function (e) { // handle error }); ``` ### Phone number A sign-in with a phone number is supported with **Firebase integration**. In order to implement authentication via phone number functionality, follow this [Firebase document](https://rnfirebase.io/auth/phone-auth). Don't forget to enable phone number sign-in for your Firebase project. To learn how to do this, see this [Firebase document](https://firebase.google.com/docs/auth/web/phone-auth#enable-phone-number-sign-in-for-your-firebase-project). To send a verification code to the user's phone and sign in the user on Firebase with the received verification code, use the snippet below. ```JavaScript JavaScript theme={null} import React, { useState } from 'react'; import { Button, TextInput } from 'react-native'; import auth from '@react-native-firebase/auth'; function PhoneSignIn() { // If null, no SMS has been sent const [confirm, setConfirm] = useState(null); const [code, setCode] = useState(''); // Handle the button press async function signInWithPhoneNumber(phoneNumber) { const confirmation = await auth().signInWithPhoneNumber(phoneNumber); setConfirm(confirmation); } async function confirmCode() { try { await confirm.confirm(code); } catch (error) { console.log('Invalid code.'); } } if (!confirm) { return ( <Button title="Phone Number Sign In" onPress={() => signInWithPhoneNumber('+1 650-555-3434')} /> ); } return ( <> <TextInput value={code} onChangeText={text => setCode(text)}/> <Button title="Confirm Code" onPress={() => confirmCode()}/> </> ); } ``` To log in the user to QuickBlox, use the `loginWithFirebase()`. The ID token of the Firebase user is received as a result of the `getIdToken()` method. ```JavaScript JavaScript theme={null} // Handle confirm code button press async function confirmCode() { try { const credential = auth.PhoneAuthProvider.credential(confirm.verificationId, code); let userData = await auth().currentUser.linkWithCredential(credential); setUser(userData.user); const idTokenResult = await firebase.auth().currentUser.getIdTokenResult(); QB.auth .loginWithFirebase("Your projectId", idTokenResult.token) .then(function (info) { // signed in successfully, handle info as necessary // info.user - user information // info.session - current session }) .catch(function (e) { // handle error }); } catch (error) { if (error.code == 'auth/invalid-verification-code') { console.log('Invalid code.'); } else { console.log('Account linking error'); } } } ``` Pass the following arguments to the `loginWithFirebase()` method. | Fields | Description | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | firebaseProjectId | Firebase project ID. When you create a Firebase project, Firebase automatically assigns a unique ID to the project, but you can edit it during the project setup. | | token | ID token of the Firebase user. Created by Firebase when a user signs in to an app. This token is received as a result of getIdToken() method. | ### Custom identity provider You can authenticate your application users from the external database with QuickBlox via Custom Identity Provider (CIdP). Just specify the user ID and access token as a password to authenticate with QuickBlox. Review [Custom Identity Provider](/docs/custom-identity-provider) page for more details on the feature. ```JavaScript JavaScript theme={null} const loginParams = { login: '4324', password: '8b75a6c7191285499d890a81df4ee7fe49bc732a' }; QB.auth .login(loginParams) .then(function (info) { // signed in successfully, handle info as necessary // info.user - user information // info.session - current session }) .catch(function (e) { // handle error }); ``` <Warning> This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com.). </Warning> ## Log out user If you have a user session, you can downgrade it to an application session by calling `logout()` method. ```JavaScript JavaScript theme={null} QB.auth .logout() .then(function () { // signed out successfully }) .catch(function (e) { // handle error }); ``` ## Set existing session Typically, a session token is stored in SDK after successful login and used for every subsequent API call. However, you may want to obtain and store the session on your server for better security. In this case, you can set [application or user token](https://docs.quickblox.com/reference/authentication#application-vs-user-session-token) into SDK using the `startSessionWithToken()` method, also before calling the method to get notified that the session has expired you need to add **SESSION\_EXPIRED** listener to **NativeEventEmitter**. Don't forget to [log in user](/sdks/react-native-authentication#log-in-user) if you pass the application token. ```JavaScript JavaScript theme={null} import QB from 'quickblox-react-native-sdk'; import { NativeEventEmitter } from 'react-native'; const emitter = new NativeEventEmitter(QB.auth); function sessionExpiredHandler(event) { // handle session expired event } emitter.addListener(QB.auth.EVENT_TYPE.SESSION_EXPIRED, sessionExpiredHandler); const sessionToken = "8b75a6c7191285499d890a81df4ee7fe49bc732a" QB.auth .startSessionWithToken(sessionToken) .then(function (session) { // handle session }) .catch(function (e) { // handle error }); ``` <Warning> If you have version lower than 8.0.0, you can set the existing session token into SDK using the `setSession()` method. </Warning> ```JavaScript JavaScript theme={null} const session = { applicationId: 76730, token: "8b75a6c7191285499d890a81df4ee7fe49bc732a", userId: 96752798, }; QB.auth .setSession(session) .then((result) => {}) .catch((error) => {}); ``` The `setSession()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ------------- | -------- | ------------------------ | | applicationId | yes | Application ID. | | token | yes | QuickBlox session token. | | userId | yes | User ID. | <Warning> You must call the `init()` method before calling the `setSession()` method. If you attempt to call the method without initializing SDK previously, the error will be returned. </Warning> # Connection Source: https://docs.quickblox.com/sdks/react-native-chat-connection Learn how to connect to the chat server and set connection settings. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/react-native-authentication) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Connect to Chat server To connect to Chat server use the code snippet below. ```JavaScript JavaScript theme={null} const chatConnectParams = { userId: 12345, password: 'passw0rd!' }; QB.chat .connect(chatConnectParams) .then(function () { // connected successfully }) .catch(function (e) { // some error occurred }); ``` ## Subscribe to connection state Subscribe to the connection state changes using the following code snippet. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from 'react-native' import QB from 'quickblox-react-native-sdk' const emitter = new NativeEventEmitter(QB.chat); const QBConnectionEvents = [ QB.chat.EVENT_TYPE.CONNECTED, QB.chat.EVENT_TYPE.CONNECTION_CLOSED, QB.chat.EVENT_TYPE.CONNECTION_CLOSED_ON_ERROR, QB.chat.EVENT_TYPE.RECONNECTION_FAILED, QB.chat.EVENT_TYPE.RECONNECTION_SUCCESSFUL, ] QBConnectionEvents.forEach(eventName => { emitter.addListener(eventName, connectionEventHandler) }) ``` ## Check if connected to Chat server Check the connection state using the following code snippet. ```JavaScript JavaScript theme={null} QB.chat .isConnected() .then(function (connected) { // boolean // handle as necessary, i.e. // if (connected === false) reconnect() }) .catch(function (e) { // handle error }); ``` | Connection Event | Description | | :------------------------------------------------ | :---------------------------------------------------------------------------------------------------------------- | | QB.chat.EVENT\_TYPE.CONNECTED | Fired when the connection to the QuickBlox server is established successfully. | | QB.chat.EVENT\_TYPE.CONNECTION\_CLOSED | Fired when the connection to the QuickBlox server is closed normally, typically by calling the disconnect method. | | QB.chat.EVENT\_TYPE.CONNECTION\_CLOSED\_ON\_ERROR | Fired when the connection to the QuickBlox server is closed due to an error. | | QB.chat.EVENT\_TYPE.RECONNECTION\_FAILED | Fired when the reconnection attempt to the QuickBlox server fails. | | QB.chat.EVENT\_TYPE.RECONNECTION\_SUCCESSFUL | Fired when the reconnection attempt to the QuickBlox server is successful. | ## Check if connected to Chat server Check the connection state using the following code snippet. ```javascript theme={null} QB.chat .isConnected() .then(function (connected) { // boolean // handle as necessary, i.e. // if (connected === false) reconnect() }) .catch(function (e) { // handle error }); ``` ## Disconnect from Chat server Disconnect from the Chat server using the snippet below. ```JavaScript JavaScript theme={null} QB.chat .disconnect() .then(function () { // disconnected successfully }) .catch(function (e) { // handle error }); ``` ## Enable auto-reconnect to Chat The SDK reconnects automatically when the connection to the Chat server is lost. There is a way to disable it and then manage it manually. ```JavaScript JavaScript theme={null} const autoReconnectParams = { enable: false }; QB.settings .enableAutoReconnect(autoReconnectParams) .then(function () { // done }) .catch(function (e) { // handle error }); ``` ## Manage Chat connections To provide a seamless chat experience, our SDK manages connections to the Chat server at an application-wide level. Thus, to handle [offline messages](/sdks/react-native-chat-offline-messaging) correctly, use the `disconnect()` method when an app goes to the background and `connect()` method when an app goes to the foreground. As a result, the SDK will disconnect/connect the current user from the Chat server when the app goes to the background/foreground mode. ```JavaScript JavaScript theme={null} import { AppState } from 'react-native' import QB from 'quickblox-react-native-sdk' AppState.addEventListener('change', trackAppState) function trackAppState (appState) { if (appState.match(/inactive|background/)) { QB.chat .disconnect() .then(function () { /* disconnected successfully */ }) .catch(function (e) { /* handle error */ }); } else { const chatConnectParams = { userId: 12345, password: 'passw0rd!' }; QB.chat .connect(chatConnectParams) .then(function () { /* connected successfully */ }) .catch(function (e) { /* some error occurred */ }); } } ``` # Contact List Source: https://docs.quickblox.com/sdks/react-native-chat-contact-list Learn how to create, update, manage, and add users to your contact list. This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://help.quickblox.com/). # Dialogs Source: https://docs.quickblox.com/sdks/react-native-chat-dialogs Learn how to create and manage dialogs. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/react-native-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/react-native-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Dialog types All chats between users are organized in dialogs. There are 3 types of dialogs: * **private dialog** - a dialog between 2 users. * **group dialog** - a dialog between the specified list of users. * **public dialog** - an open dialog. Any user from your app can be added to it. You need to create a new dialog and then use it to chat with other users. You also can obtain a list of your existing dialogs. ## Create dialog To create a **private** dialog, you need to set the ID of the opponent you want to create a chat with. ```JavaScript JavaScript theme={null} const createDialogParam = { type: QB.chat.DIALOG_TYPE.CHAT, occupantsIds: [12345] }; QB.chat .createDialog(createDialogParam) .then(function (dialog) { // handle as neccessary, i.e. // subscribe to chat events, typing events, etc. }) .catch(function (e) { // handle error }); ``` To create a **group** dialog for a predefined number of occupants, you need to set IDs of opponents you want to create a chat with. ```JavaScript JavaScript theme={null} const createDialogParam = { type: QB.chat.DIALOG_TYPE.GROUP_CHAT, name: 'Group Chat', occupantsIds: [12345, 12346, 12347], photo: "some photo url" }; QB.chat .createDialog(createDialogParam) .then(function (dialog) { // handle as neccessary, i.e. // subscribe to chat events, typing events, etc. }) .catch(function (e) { // handle error }); ``` It's possible to create a **public** dialog, so any user from your application can be joined to it. There is no list of occupants. This dialog is open for everybody. ```JavaScript JavaScript theme={null} const createDialogParam = { type: QB.chat.DIALOG_TYPE.PUBLIC_CHAT, name: 'Awesome Public Chat' }; QB.chat .createDialog(createDialogParam) .then(function (dialog) { // handle as neccessary, i.e. // subscribe to chat events, typing events, etc. }) .catch(function (e) { // handle error }); ``` The `createDialog()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | name | yes | A name of the dialog. Required only for **group** and **public** dialog types. Not needed for **private** dialog. | | occupantsIds | yes | A list of opponents IDs.- If the occupantsIds array is empty and type is not provided, the **public** dialog is created.- If the occupantsIds has a single user and type is not provided, the **private** dialog is created.- If the occupantsIds has more then one userId and type is not provided, a **group** dialog is created. | | type | no | A type of the dialog. Possible values: `QB.chat.DIALOG\_TYPE.CHAT`, `QB.chat.DIALOG\_TYPE.GROUP\_CHAT` or `QB.chat.DIALOG\_TYPE.PUBLIC\_CHAT`. By default, the **public** dialog is created. | | photo | no | A URL of the image. Can be a link to a file in Content module, Custom Objects module or just a web link. **Must** be a String. | ## Create dialog with custom parameters A dialog can be extended with additional parameters. These parameters can be used to store additional data. Also, these parameters can be used in dialogs retrieval requests. To start using additional parameters, create an additional schema of your parameters. This is a custom objects class. Just create an empty class with all fields that you need. These fields will be additional parameters in your dialog. See [this section](/sdks/react-native-custom-objects) to learn how to create a schema using Custom Objects. Then, specify the parameters defined in the schema in a new dialog. ```JavaScript JavaScript theme={null} const createDialogParam = { type: QB.chat.DIALOG_TYPE.GROUP_CHAT, occupantsIds: [12345, 12346, 12347], name: 'My friends', customData: { "class_name":"CoolDialog", "category":"friends" } }; QB.chat .createDialog(createDialogParam) .then(function (dialog) { // handle as neccessary, i.e. // subscribe to chat events, typing events, etc. }) .catch(function (e) { // handle error }); ``` ## Join dialog Before you start chatting in a **group** or **public** dialog, you need to join it by calling the `joinDialog()` method. If you've successfully joined the dialog, you can send/receive messages in real-time. See [this section](/sdks/react-native-chat-messaging#send-text-message) to learn how to send/receive messages. ```JavaScript JavaScript theme={null} const joinDialogParam = { dialogId: 'dsfsd934329hjhkda98793j2' }; // First check if the dialog is joined using QB.chat.isJoinedDialog QB.chat.isJoinedDialog(joinDialogParam) .then(isJoined => { if (isJoined) { return; } QB.chat .joinDialog(joinDialogParam) .then(function () { // Handle successful leave }) .catch(function (e) { // Handle error }); }) .catch(function (e) { // Handle error checking if the dialog is joined }); ``` Let's see, how the `join()` method is used with regard to the dialog type. | Capabilities | Public | Group | Private | | ------------ | ------ | ----- | ------- | | Join | ✓ | ✓ | ✗ | <Note> You can join a group dialog **only** if your user ID is present in the `occupantsIds` array, in the dialog model. Your user ID is added to the `occupantsIds` array if you create a dialog **or** you are added to the dialog by the other user. See [this section](/sdks/react-native-chat-dialogs#add-occupants) to learn how to add/remove occupants to the group dialog. </Note> ## Leave dialog You can leave the **group** and **public** dialog by calling the `leaveDialog()` method. If the dialog is left, you can't send/receive messages. To be able to receive/send messages, you need to join it. ```JavaScript JavaScript theme={null} const leaveDialogParam = { dialogId: 'dsfsd934329hjhkda98793j2' }; // First check if the dialog is joined using QB.chat.isJoinedDialog QB.chat.isJoinedDialog(leaveDialogParam) .then(isJoined => { if (isJoined === false) { return; } QB.chat .leaveDialog(leaveDialogParam) .then(function () { // Handle successful leave }) .catch(function (e) { // Handle error }); }) .catch(function (e) { // Handle error checking if the dialog is joined }); ``` Let's see, how the `leaveDialog()` method is used with regard to the dialog type. | Capabilities | Public | Group | Private | | ------------ | ------ | ----- | ------- | | Leave | ✓ | ✓ | ✗ | <Note> When a **group** dialog is left, your user ID is removed `occupantsIds` array, in the dialog model. As a result, the dialog is removed from the list of dialogs and you won't have access to the chat history. To remove a dialog for all users, use the `deleteDialog()` method. See [this section](/sdks/react-native-chat-dialogs#delete-dialog) to learn how to delete the dialog completely for all users. </Note> ## Retrieve list of dialogs It's common to request all your dialogs or only a number of recently updated dialogs on every app login. The request below will return a list of **private**, **group**, and **public** dialogs containing `chat` in their names, sorted by the `QB.chat.DIALOGS_SORT.FIELD.LAST_MESSAGE_DATE_SENT` field in ascending order, and limited to 10 dialogs on the page. ```JavaScript JavaScript theme={null} // * @param {Object} result // * @param {Dialog[]} result.dialogs // * @param {number} result.skip // * @param {number} result.limit // * @param {number} result.total function processDialogs(result) { // dialogs found matching filter and sort } // get dialogs with 'chat' in name const filter = { field: chat.DIALOGS_FILTER.FIELD.NAME, operator: chat.DIALOGS_FILTER.OPERATOR.CTN, value: 'chat' }; // sorted ascending by "last_message_date_sent" const sort = { field: QB.chat.DIALOGS_SORT.FIELD.LAST_MESSAGE_DATE_SENT, ascending: true }; const getDialogsQuery = { filter: filter, sort: sort, limit: 10, skip: 0 }; QB.chat .getDialogs(getDialogsQuery) .then(processDialogs) .catch(function (e) { // handle error }); ``` The method `getDialogs()` accepts one (optional) argument of the object type that has the following fields: | Field | Required | Description | | ------ | -------- | ------------------------------------------------------------------------------------------- | | filter | no | Specifies filtering criteria for the field. | | sort | no | Specifies sorting criteria for the field. | | skip | no | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | no | Limit search results to N records. Useful for pagination. Default value: **100**. | If you want to retrieve only dialogs updated after some specific date time and order the search results, you can apply operators. This is useful if you cache dialogs somehow and do not want to obtain the whole list of your dialogs on every app start. Thus, you can apply [search](/sdks/react-native-users#search-operators) and [sort](/sdks/react-native-users#sort-operators) operators to list dialogs on the page so that it is easier to view specific dialogs. ### Search operators You can use search operators to get more specific search results. The request below will return all **private** and **group** dialogs excluding **public** dialogs. ```JavaScript JavaScript theme={null} function processDialogs(result) {} // get dialogs excluding public chats const filter = { field: QB.chat.DIALOGS_FILTER.FIELD.TYPE, operator: QB.chat.DIALOGS_FILTER.OPERATOR.NE, value: QB.chat.DIALOG_TYPE.PUBLIC_CHAT.toString() }; const getDialogsQuery = { filter: filter, }; QB.chat .getDialogs(getDialogsQuery) .then(processDialogs) .catch(function (e) { // handle error }); ``` Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | --------------------------------------------------- | ---------------------------------------------------- | | lt | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** operator. | | lte | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Less Than** or **Equal** to operator. | | gt | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** operator. | | gte | number, string, date | last\_message\_date\_sent, created\_at, updated\_at | **Greater Than** or **Equal** to operator. | | ne | number, string, date | \_id, name, last\_message\_date\_sent | **Not Equal** to operator. | | in | number, string, date | type, last\_message\_date\_sent, name | **IN** array operator. | | nin | number, string, date | last\_message\_date\_sent | **IN** array operator. | | all | number | occupants\_ids | **ALL** are contained in array. | | ctn | string | name | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return dialogs sorted in ascending order by the `QB.chat.DIALOGS_SORT.FIELD.LAST_MESSAGE_DATE_SENT` field. ```JavaScript JavaScript theme={null} function processDialogs(result) {} const sort = { field: QB.chat.DIALOGS_SORT.FIELD.LAST_MESSAGE_DATE_SENT, ascending: true }; const getDialogsQuery = { sort: sort, }; QB.chat .getDialogs(getDialogsQuery) .then(processDialogs) .catch(function (e) { // handle error }); ``` Here are the sort options that you can use to order search results. | Sort options | Applicable to types | Applicable to fields | Description | | ------------ | ------------------- | ------------------------------------------------ | ----------------------------------------------------------------------- | | ascending | All types | id, created\_at, name, last\_message\_date\_sent | Sort results in the ascending order by setting the ascending as true. | | descending | All types | id, created\_at, name, last\_message\_date\_sent | Sort results in the descending order by setting the ascending as false. | ## Retrieve dialogs by ID You can get a list of dialogs by their IDs using the `getDialogs()` method. The request below will return the specified dialogs limited to 5 dialogs per page with 2 dialogs skipped at the beginning of the page. ```JavaScript JavaScript theme={null} const dialogsIds = [ '1234567890', '1234567891', '1234567892', '1234567893', '1234567894', '1234567895', '1234567896', '1234567897', '1234567898', '1234567899', ]; const filter = { field: QB.chat.DIALOGS_FILTER.FIELD.ID, operator: QB.chat.DIALOGS_FILTER.OPERATOR.IN, value: dialogsIds.join() }; const getDialogsQuery = { filter: filter, limit: 5, skip: 2 }; QB.chat .getDialogs(getDialogsQuery) .then(function (result) { // dialogs found matching filter and sorted }) .catch(function (e) { // handle error }); ``` ## Update dialog You can update the information for a **private**, **group**, and **public** dialog. ```JavaScript JavaScript theme={null} const updateDialogParam = { dialogId: 'dsfsd934329hjhkda98793j2', name: 'Team room' }; QB.chat .updateDialog(updateDialogParam) .then(function (updatedDialog) { // handle as necessary }) .catch(function (e) { // handle error }); ``` Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | ------------------------ | -------------- | -------------- | -------------- | | Update a dialog name | Owner | Owner | ✗ | | Update a photo | Owner | Owner | ✗ | | Update custom parameters | Owner,Occupant | Owner,Occupant | Owner,Occupant | ## Add occupant Set the `addUsers` field to add occupants to the dialog. As a result, the occupant ID will be added to the `occupantsIds` array. ```JavaScript JavaScript theme={null} const updateDialogParam = { dialogId: 'dsfsd934329hjhkda98793j2', addUsers: [12340], }; QB.chat .updateDialog(updateDialogParam) .then(function (updatedDialog) { // handle as necessary }) .catch(function (e) { // handle error }); ``` The `updateDialog()` method accepts one argument of the object type that has the following fields: | Fields | Required | Description | | -------- | -------- | ------------------------------------------- | | dialogId | yes | ID of the dialog | | addUsers | yes | IDs of occupants to be added to the dialog. | Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | --------------- | ------------- | -------------- | -------------- | | Add other users | ✗ | Owner,Occupant | ✗ | ## Remove occupant Set the `removeUsers` field remove occupants from a **group** dialog. As a result, the occupant ID will be removed from the `occupantsIds` array. ```JavaScript JavaScript theme={null} const updateDialogParam = { dialogId: 'dsfsd934329hjhkda98793j2', removeUsers: [12345], }; QB.chat .updateDialog(updateDialogParam) .then(function (updatedDialog) { // handle as necessary }) .catch(function (e) { // handle error }); ``` The `updateDialog()` method accepts one argument of the object type that has the following fields: | Fields | Required | Description | | ----------- | -------- | ----------------------------------------------- | | dialogId | yes | ID of the dialog | | removeUsers | yes | IDs of occupants to be removed from the dialog. | Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public dialog | Group dialog | Private dialog | | ------------------ | ------------- | -------------- | -------------- | | Remove other users | ✗ | Owner | ✗ | | Remove yourself | ✗ | Owner,Occupant | ✗ | ## Delete dialog Use the `deleteDialog()` method to delete a dialog for all users. When deleting a **group** dialog, all user IDs will be removed from the `occupantsIds` array in the dialog model. You can also delete multiple dialogs in a single request. To delete a dialog for yourself, just leave the dialog. See [this section](/sdks/react-native-chat-dialogs#leave-dialog) for more information. ```JavaScript JavaScript theme={null} const deleteDialogParam = { dialogId: 'dsfsd934329hjhkda98793j2' }; QB.chat .deleteDialog(deleteDialogParam) .then(function () { // dialog was removed successfully }) .catch(function (e) { // handle error }); ``` Let's see what capabilities a particular user role has with regard to the dialog type. | Capabilities | Public | Group | Private | | ----------------------------- | ------ | ----- | ------- | | Delete a dialog for all users | Owner | Owner | Owner | ## Resources A sequence of steps a user takes to start a dialog by moving through the application lifecycle. <img alt="react-native-starting-dialog.jpg" /> # Messaging Source: https://docs.quickblox.com/sdks/react-native-chat-messaging Learn how to send and receive messages, mark messages as delivered or read, etc. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/react-native-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/react-native-chat-connection) page to learn how to do it. 5. Create a dialog. See [Dialogs](/sdks/react-native-chat-dialogs) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Subscribe message events Add the event handler to receive messages in real-time. The event handler enables the app to receive message events associated with receiving a message, delivery receipts, and read receipts. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from "react-native"; import QB from "quickblox-react-native-sdk"; const emitter = new NativeEventEmitter(QB.chat); function receivedNewMessage(event) { const { type, payload } = event; // handle new message // type - event name (string) // payload - message received (object) } function messageStatusHandler(event) { // handle message status change } function systemMessageHandler(event) { // handle system message } function userTypingHandler(event) { // handle user typing / stopped typing event } emitter.addListener( QB.chat.EVENT_TYPE.RECEIVED_NEW_MESSAGE, receivedNewMessage ); emitter.addListener(QB.chat.EVENT_TYPE.MESSAGE_DELIVERED, messageStatusHandler); emitter.addListener(QB.chat.EVENT_TYPE.MESSAGE_READ, messageStatusHandler); emitter.addListener(QB.chat.EVENT_TYPE.RECEIVED_SYSTEM_MESSAGE, systemMessageHandler); emitter.addListener(QB.chat.EVENT_TYPE.USER_IS_TYPING, userTypingHandler); emitter.addListener(QB.chat.EVENT_TYPE.USER_STOPPED_TYPING, userTypingHandler); ``` ## Send text message To send a message to a **private** dialog, use the code snippet below. ```JavaScript JavaScript theme={null} const message = { dialogId: 'dsfsd934329hjhkda98793j2', body: 'Hey there!', saveToHistory: true }; QB.chat .sendMessage(message) .then(function () { /* send successfully */ }) .catch(function (e) { /* handle error */ }) ``` <Note> You need to join the **public** and **group** dialog by calling the `join()` method before you start chatting in a dialog. Once the dialog is joined, you can receive/send messages. See [this section](/sdks/react-native-chat-dialogs#join-dialog) to learn how to join the dialog. </Note> To send messages to a **group** or **public dialog**, use the code snippet below. ```JavaScript JavaScript theme={null} const message = { dialogId: 'dsfsd934329hjhkda98793j2', body: 'Hey there!', saveToHistory: true }; QB.chat .sendMessage(message) .then(function () { /* send successfully */ }) .catch(function (e) { /* handle error */ }) ``` Use the same code snippet to send/receive messages for **private**, **group**, and **public** dialog. <Note> Make sure to set the `saveToHistory` as `true` to save the message on the server. If the `saveToHistory` is set as `false`, the message won't be saved on the server. However, the message will be delivered to the user in either case. </Note> ## Send message with attachment Chat attachments are supported by the [content API](/sdks/react-native-content). In order to send a chat attachment, you need to upload the file to QuickBlox cloud storage and obtain a link to the file (file UID). Then you need to include this UID into the chat message and send it. ```JavaScript JavaScript theme={null} const contentUploadParams = { url: "...", // path to file in local file system public: false, }; QB.content .upload(contentUploadParams) .then(function (file) { // create a message const message = { attachments: [], dialogId: "dsfsd934329hjhkda98793j2", body: "Hey there!", saveToHistory: true, }; // attach file message.attachments.push({ id: file.uid, type: file.contentType.includes("image") ? "image" : "file", }); // send a message }) .catch(function (e) { /* handle file upload error */ }); ``` The flow on the receiver's side is the following: when you receive a message, you need to get the file URL to download the file from the cloud storage. ```JavaScript JavaScript theme={null} // received message const { attachments } = message const [attachment] = attachments const contentGetFileUrlParams = { uid: attachment.id }; QB.content .getPrivateURL(contentGetFileUrlParams) .then(function (url) { /* you download file using obtained url */ }) .catch(function (e) { /* handle error */ }); ``` ## Send message with extra data You have an option to extend the message with additional fields. Specify one or more **key-value** items in the `properties`. Using these items, you can implement the ability for a user to send self-location information to another user or notification messages signifying that a user has left a group, etc. ```JavaScript JavaScript theme={null} const message = { dialogId: 'dsfsd934329hjhkda98793j2', body: 'How are you today!', properties: { customParam1: "book", customParam2: "21" }, saveToHistory: true }; QB.chat .sendMessage(message) .then(function () { /* send successfully */ }) .catch(function (e) { /* handle error */ }) ``` The `sendMessage()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- | | dialogId | yes | The ID of a dialog. | | body | no | A message text. | | properties | no | Extra data. Specify any key-value pairs. In each pair, the key and value are both string values. | | saveToHistory | no | Specifies if the message will be saved on the server. Set the saveToHistory as true to save the message on the server. | ## Retrieve chat history Every dialog stores its chat history that you can retrieve using the `getDialogMessages()` method. The request below will return messages for a specific dialog, sorted by the `date_sent` field in descending order. ```JavaScript JavaScript theme={null} const getDialogMessagesParams = { dialogId: 'dsfsd934329hjhkda98793j2', sort: { ascending: false, field: QB.chat.MESSAGES_SORT.FIELD.DATE_SENT }, markAsRead: false }; QB.chat .getDialogMessages(getDialogMessagesParams) .then(function (result) { // result.messages - array of messages found // result.skip - number of items skipped // result.limit - number of items returned per page }) .catch(function (e) { // handle error }); ``` <Warning> If you want to mark all retrieved chat messages as a read, set the `markAsRead` parameter as `true`. If you decide not to mark chat messages as read, just set `markAsRead` parameter as `false` or omit the parameter. </Warning> If you want to retrieve only messages updated after some specific date time and order the search results, you can apply operators. This is useful if you cache messages somehow and do not want to obtain the whole list of messages on every app start. Thus, you can apply [search](/sdks/react-native-chat-messaging#search-operators) and [sort](/sdks/react-native-chat-messaging#sort-operators) operators to list messages on the page so that it is easier to view specific messages. If you want to get a paginated list of messages from the server, you can set the following pagination parameters: | Pagination parameters | Description | | --------------------- | ------------------------------------------------------------------------------------------- | | skip | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | Limit search results to N records. Useful for pagination. Default value: **100**. | ### Search operators You can use search operators to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | ---------------------------------------------------- | ---------------------------------------------------- | | lt | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** operator. | | lte | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Less Than** or **Equal** to operator. | | gt | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** operator. | | gte | number, string, date | date\_sent, sender\_id, recipient\_id, updated\_at | **Greater Than** or **Equal** to operator. | | ne | number, string, date | \_id, message, date\_sent, sender\_id, recipient\_id | **Not Equal** to operator. | | in | number, string, date | date\_sent, sender\_id, recipient\_id | **IN** array operator. | | nin | number, string, date | date\_sent, sender\_id, recipient\_id | Not **IN** array operator. | | or | number, string, date | date\_sent, sender\_id, recipient\_id | All records that contain a value 1 **or** value 2. | | ctn | string | message | All records that **contain** a particular substring. | ### Sort operators Here are the sort options that you can use to order search results. | Sort options | Applicable to types | Description | | ------------ | ------------------- | ----------------------------------------------------------------------- | | ascending | All types | Sort results in the ascending order by setting the ascending as true. | | decsending | All types | Sort results in the descending order by setting the ascending as false. | ## Check if a message is sent The message is considered as **sent** if it has been delivered to the server. To get to know that a message has been delivered to the server, make sure to enable a stream management before connecting to the Chat server. See [this section](/sdks/react-native-setup#stream-management) to learn how to enable the stream management. Thus, you send a message to the server and if no error is returned, it is considered as **sent** (by default). There is no field for a sent status in the message model. ```JavaScript JavaScript theme={null} QB.chat .sendMessage(message) .then(function () { /* message sent */ }) .catch(function (e) { /* message is not sent */ }); ``` <Warning> You should enable Stream Management before you do the `login()` because the Stream Management is initialized while Chat login is performed. The Stream Management defines an extension for active management of a stream between a client and server, including features for stanza acknowledgments. </Warning> ## Mark message as delivered As a sender, you may want to be informed that a message has been successfully delivered to the recipient. The mark-as-delivered functionality allows to notify the sender about message delivery. To track the event when the message has been delivered to the user, use the event listener. As a result, when a user receives a message, the SDK receives the `QB.chat.EVENT_TYPE.MESSAGE_DELIVERED` event. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from "react-native"; import QB from "quickblox-react-native-sdk"; const emitter = new NativeEventEmitter(QB.chat); function messageDelivered(event) { const { type, // name of the event (the one you've subscribed for) payload, // event data } = event; const { dialogId, // in dialog with id specified messageId, // message with id specified userId, // was delivered to user with id specified } = payload; // handle as necessary } emitter.addListener(QB.chat.EVENT_TYPE.MESSAGE_DELIVERED, messageDelivered); ``` Use the `markMessageDelivered()` method to mark a message as delivered. As a result, the server will notify a sender about the delivery receipt. ```JavaScript JavaScript theme={null} const markMessageDeliveredParams = { message: { id: 'ghtsd934679hjhkda98793t4', dialogId: 'dsfsd934329hjhkda98793j2', senderId: 12345, }, }; QB.chat .markMessageDelivered(markMessageDeliveredParams) .then(function () { /* marked as "delivered" successfully */ }) .catch(function (e) { /* handle error */ }); ``` A message can be marked as delivered automatically by the server once a message is successfully delivered to the recipient. Set the `markable` as `true` using the `sendMessage()` method if you want, as a sender, to receive message delivery receipts from other recipients. Thus, the `markable` parameter enables the sender to request the delivery receipt. It also enables the recipient to confirm the message delivery. However, if `markable` is `false` or omitted, then you can notify a sender about the delivery receipt using the `markMessageDelivered()` method. ```JavaScript JavaScript theme={null} const message = { dialogId: 'dsfsd934329hjhkda98793j2', body: 'Hello!', markable: true }; QB.chat .sendMessage(message) .then(function () { /* message sent */ }) .catch(function (e) { /* message is not sent */ }); ``` <Note> Make sure to understand, that marking-as-delivered operation just confirms the fact of message delivery. The message acquires the **delivered** status when the `QB.chat.EVENT_TYPE.MESSAGE_DELIVERED` event is received. When a message is marked as delivered, the IDs of users who have received the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who received the message using the `getDialogMessages()` method. See [this section](/sdks/react-native-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Note> ## Mark message as read As a sender, you may want to be informed that a message has been read by the recipient. The mark-as-read functionality allows to notify the sender that a message has been read. To track the event when the message has been read by the user, you need to subscribe to this event using the code snippet below. As a result, when a user reads a message, the SDK receives the `QB.chat.EVENT_TYPE.MESSAGE_READ` event. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from "react-native"; import QB from "quickblox-react-native-sdk"; const emitter = new NativeEventEmitter(QB.chat); function messageRead(event) { const { type, // name of the event (the one you've subscribed for) payload, // event data } = event; const { dialogId, // in dialog with id specified messageId, // message with id specified userId, // was delivered to user with id specified } = payload; // handle as necessary } emitter.addListener(QB.chat.EVENT_TYPE.MESSAGE_READ, messageRead); ``` Use the `markMessageRead()` method to mark a message as read. As a result, the server will notify a sender about the read receipt. ```JavaScript JavaScript theme={null} const markMessageReadParams = { message: { id: "ghtsd934679hjhkda98793t4", dialogId: "dsfsd934329hjhkda98793j2", senderId: 12345, }, }; QB.chat .markMessageRead(markMessageReadParams) .then(function () { /* marked as "read" successfully */ }) .catch(function (e) { /* handle error */ }); ``` <Note> When a message is marked as read, the IDs of users who have read the message are stored in the message model, on the server. Thus, you can request a chat history from the server to get to know who read the message using the `getDialogMessages()` method. See [this section](/sdks/react-native-chat-messaging#retrieve-chat-history) to learn how to retrieve chat history. </Note> ## Send typing indicators You may want, as a sender, to let the recipient know that you are typing the message or have stopped typing the message. Use typing indicators as a form of chat-specific presence. Typing indicators allow to indicate if users are typing messages in a dialog at the moment. There are the following **typing** notifications supported. * **typing**. The user is composing a message. The user is actively interacting with a message input interface specific to this chat session (for example, by typing in the input area of a chat window). * **stopped**. The user had been composing but now has stopped. The user has been composing but has not interacted with the message input interface for a short period of time (for example, 30 seconds). To track the event when the sender is typing the message, use the event listener. As a result, when a sender is typing a message, the SDK receives the `QB.chat.EVENT_TYPE.USER_IS_TYPING` event. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from "react-native"; import QB from "quickblox-react-native-sdk"; const emitter = new NativeEventEmitter(QB.chat); function userTypingHandler(event) { const { type, // name of the event (the one you've subscribed for) payload, // event data } = event; const { dialogId, // in dialog with id specified userId, // user with id specified is typing } = payload; // handle as necessary } emitter.addListener(QB.chat.EVENT_TYPE.USER_IS_TYPING, userTypingHandler); ``` To track the event when the sender has stopped typing, use the event listener. Thus, when a sender has stopped typing a message, the SDK receives the `QB.chat.EVENT_TYPE.USER_STOPPED_TYPING` event. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from "react-native"; import QB from "quickblox-react-native-sdk"; const emitter = new NativeEventEmitter(QB.chat); function userStoppedTypingHandler(event) { const { type, // name of the event (the one you've subscribed for) payload, // event data } = event; const { dialogId, // in dialog with id specified userId, // user with id specified stopped typing } = payload; // handle as necessary } emitter.addListener( QB.chat.EVENT_TYPE.USER_STOPPED_TYPING, userStoppedTypingHandler ); ``` To notify a recipient that a sender is typing the message, use the `sendIsTyping()` method. As a result, the server will notify a recipient about the event. ```JavaScript JavaScript theme={null} const isTypingParams = { dialogId: "dsfsd934329hjhkda98793j2" }; QB.chat .sendIsTyping(isTypingParams) .then(function () { /* sent successfully */ }) .catch(function (e) { /* handle error */ }); ``` To notify a recipient that a sender had been composing a message but now has stopped, use the `sendStoppedTyping()` method. As a result, the server will notify a recipient about the event. ```JavaScript JavaScript theme={null} const isTypingParams = { dialogId: "dsfsd934329hjhkda98793j2" }; QB.chat .sendStoppedTyping(isTypingParams) .then(function () { /* sent successfully */ }) .catch(function (e) { /* handle error */ });) ``` ## Send system messages There is a way to send system messages to other users about some events. For example, a system message can be sent when a user has joined or left a group dialog. These messages are handled over a separate channel and are not be mixed up with regular chat messages. Thus, in order to receive these messages, you should subscribe to the `QB.chat.EVENT_TYPE.RECEIVED_SYSTEM_MESSAGE` event. See [this section](/sdks/react-native-chat-messaging#subscribe-message-events) to learn how to subscribe to this event. System messages are also not shown in the dialog history and, consequently, are not stored on the server. This means that these messages will be delivered **only** to online users. Send system messages using the `sendSystemMessage()` method. ```JavaScript JavaScript theme={null} const systemMessage = { recipientId: 1234567, properties: { notification_type: "1", dialog_id: "5d75393ba28f9a17e1cb0f9e", }, }; QB.chat.sendSystemMessage(systemMessage); ``` The `sendSystemMessage()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | recipientId | yes | ID of the recipient. | | properties | no | Extra data. Specify any key-value pairs. In each pair, the key and value are both string values. You can't specify any object, because it won't be passed as an argument. | # Offline Messaging Source: https://docs.quickblox.com/sdks/react-native-chat-offline-messaging Learn how to receive push notifications when the app is not running. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/react-native-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/react-native-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Enable offline messaging Send notifications about new messages to the user when their app is not running by using offline messaging functionality. This functionality allows to customize automatic push notifications displayed on a user's device when they are offline. In other words, if your opponent is offline when you are writing a message, they automatically receive a push notification. <Note> Make sure to subscribe your users to pushes using SDKs. Review [Push Notifications](/sdks/ios-push-notifications) section for more details. </Note> <Warning> Only FCM data messages can be sent from the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages in your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notifications. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> Enable offline messaging from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Check the **Enable automatic push notifications for offline users** if you want to receive push notifications from the server automatically. <Frame> <img alt="enable offline messaging.png" /> </Frame> ## Configure offline messaging template You can configure an offline messaging template from the Dashboard as follows: 1. Navigate to the **Dashboard => *YOUR\_APP* => Chat => Offline messaging** page to locate offline messaging settings. 2. Choose the template in the **Templates** section. <Note> Available for Growth/HIPAA Cloud/Enterprise plan You can set the custom template in the **Offline messaging text** section using the following template variables: * %unread\_count% - total number of new messages the recipient has * %plural\[new message]% - words in brackets (20 characters max) will be pluralized based on unread messages count * %body\[x]% - quote x characters from message body * %recipient\_fullname% - full name of recipient * %sender\_fullname% - full name of sender * %recipient\_login% - login of recipient * %sender\_login% - login of sender * %dialog\_name% - name of chat dialog (will be replaced with "Private dialog" string for 1-1 messages) </Note> 1. Set **Badge counter** to include counter info into your push message. Useful to include an unread counter that is the number of unread messages a user has. 2. Set push notification sound in the **Sound** section (for iOS only). 3. Set the **Category (iOS)** field. Enter "actions" that should be presented in the app notification in various views (for iOS only). 4. Enable the **Content Available** option to indicate that new content is available (for iOS only). 5. Enable the **Mutable content** option to modify the content of newly delivered notifications before the user is alerted. Use a notification service app extension to be able to modify the content of the notification. If the option is enabled, the system passes the notification to the service app extension for the subsequent modification of the notification payload before the actual delivery (for iOS only). 6. Click the **Save** button. <Frame> <img alt="configure template.png" /> </Frame> <Warning> Currently, push notifications are supported in a mobile environment only. </Warning> # Privacy List Source: https://docs.quickblox.com/sdks/react-native-chat-privacy-list Learn how to create privacy lists and implement user-to-user blocks. This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://help.quickblox.com/). # User Presence Source: https://docs.quickblox.com/sdks/react-native-chat-user-presence Learn how to track user presence updates and check user status using ping. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/react-native-authentication) page to learn how to do it. 4. Connect to the Chat server. See [Connection](/sdks/react-native-chat-connection) page to learn how to do it. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Check if a user is online <Warning> This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://assist.quickblox.com/). </Warning> ## Ping user QuickBlox SDK can send application-level pings to the user or server. As a result, you can check if a user is connected to the Chat server and if there is a connection with the Chat server. ```JavaScript JavaScript theme={null} const userId = 12345; QB.chat .pingUser(userId) .then(function () { /* success */ }) .catch(function (error) { /* handle error */ }); ``` | Argument | Required | Description | | -------- | -------- | --------------- | | userId | yes | ID of the user. | ## Ping server QuickBlox SDK can send application-level pings to a server. As a result, you can check if the user is connected to the Chat server and if there is a connection with the Chat server. ```JavaScript JavaScript theme={null} QB.chat .pingServer() .then(function () { /* success */ }) .catch(function (error) { /* handle error */ }); ``` # Content Source: https://docs.quickblox.com/sdks/react-native-content Learn how to store and access files with QuickBlox file storage. The content module allows storing rich chat attachments, app content, and settings without having to republish them. Using a web interface you or your clients can control and make instant changes to the apps. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/react-native-authentication) page to learn how to do it. ## Upload file Upload a file to the cloud using the `upload()` method below. ```JavaScript JavaScript theme={null} const contentUploadParams = { url: 'path to file in local filesystem', public: false }; QB.content .upload(contentUploadParams) .then(function (file) { /* file uploaded successfully */ }) .catch(function (e) { /* handle error */ }) ``` The `upload()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | url | yes | URL of a file. | | public | no | Boolean parameter. Specifies file visibility. If the file is public then it's possible to download it without a session token. Default: **false**. | In order to track upload progress for a particular file, you should subscribe to upload progress events using the `QB.content.subscribeUploadProgress()` method. This method will emit `QB.content.EVENT_TYPE.FILE_UPLOAD_PROGRESS` event each time an upload progress for this file changes. Once the upload is finished, you can unsubscribe from upload progress events using the `QB.content.unsubscribeUploadProgress()` method. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from 'react-native'; import QB from 'quickblox-react-native-sdk'; // * @param {Object} event // * @param {'@QB/FILE_UPLOAD_PROGRESS'} event.type Type of the event. For this event it will always be `QB.content.EVENT_TYPE.FILE_UPLOAD_PROGRESS` // * @param {Object} event.payload // * @param {string} event.payload.url URL (path in filesystem) of the file being uploaded // * @param {number} event.payload.progress Progress value function uploadProgressChangeHandler(event) { const { type, payload } = event; // handle upload progress } const contentEmitter = new NativeEventEmitter(QB.content); const subscription = contentEmitter.addListener( QB.content.EVENT_TYPE.FILE_UPLOAD_PROGRESS, uploadProgressChangeHandler ); // path to a file picked from file system const url = 'file path'; const subscribeProgressParam = { url }; const contentUploadParam = { url, public: false }; QB.content .subscribeUploadProgress(subscribeProgressParam) .then(function () { // subscribed to upload progress events for this file return QB.content.upload(contentUploadParam) }) .then(function (file) { // file uploaded successfully // unscubscribe from upload progress events for this file return QB.content.unsubscribeUploadProgress() }) .then(function () { // unsubscribed from upload progress events for this file // remove subscription if it is not needed subscription.remove(); }) .catch(function (error) { // handle error }); ``` The maximum size of the uploaded file depends on the membership plan. | Basic | Startup | Growth | HIPAA | Enterprise | | | --------------- | ------- | ------ | ----- | ---------- | --------------------------------------------------------------- | | File size limit | 10 Mb | 25 Mb | 50Mb | 50Mb | [Contact our sales team](https://quickblox.com/enterprise/#get) | ## Get file info Get information about a file by ID using the `getInfo()` method below. This method allows to load the file description model from the server, not the content of the file itself. ```JavaScript JavaScript theme={null} const params = { id: 1234567 }; QB.content .getInfo(params) .then((file) => { // process result }) .catch(e => { // handle error }) ``` The `getInfo()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ----- | -------- | --------------- | | id | yes | ID of the file. | ## Get file URL There are two types of file URLs that can be obtained: private and public. * **Public URL** allows anyone to access the file, no authorization token is needed. * **Private URL** can be accessed only by QuickBlox user with a session token. ### Get public URL To receive a public URL, use the code snippet below. ```JavaScript JavaScript theme={null} // using file retrieved previously const getUrlParam = { uid: file.uid }; QB.content .getPublicUrl(getUrlParam) .then(function (url) { /* handle as necessary */ }) .catch(function (e) { /* handle error */ }) ``` The `getPublicUrl()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ----- | -------- | ------------------------------------------------------------------------------------------------------------ | | uid | yes | File unique identifier. You can take the `uid` from the file recieved as a result of the `getInfo()` method. | ### Get private URL To get a private URL of the uploaded file, use the following code snippet. ```JavaScript JavaScript theme={null} // using file retrieved previously const getUrlParam = { uid: file.uid }; QB.content .getPrivateURL(getUrlParam) .then(function (url) { /* handle as necessary */ }) .catch(function (e) { /* handle error */ }) ``` The `getPrivateURL()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ----- | -------- | -------------------------------------------------------------------------------------------------------- | | uid | yes | File unique identifier. You can take the UID from the file recieved as a result of the getInfo() method. | # Custom Objects Source: https://docs.quickblox.com/sdks/react-native-custom-objects Learn how to store and sync data with QuickBlox key-value storage. Custom Objects module provides flexibility to define any data structure (schema) you need, build one-to-many relations between schemas and control permissions for all operations made on data. Schema is defined in QuickBlox Dashboard. There are two key concepts in Custom Objects: \- **Class** represents your schema and contains field names and types. \- **Record** represents the data you put into your schema. **Class** and **Record** are similar to table and row in relational database. Every class in Custom Object module comes with five mandatory predefined fields: `_id`, `user_id`, `parent_id`, `created_at`, and `updated_at`. Allowed data types: **Integer** (or Array of Integer); **String** (or Array of String); **Float** (or Array of Float); **Boolean** (or Array of Boolean); **Location** (Array of \[\< longitude >, \< latitude >]); **File**; **Date**. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authenfication](/sdks/react-native-authentication) page to learn how to do it. ## Create class To start using Custom Objects module, create a class: 1. Go to [QuickBlox Dashboard](https://admin.quickblox.com/signin). 2. Follow **Custom => Add => Add new class** direction. As a result, **Add new class** popup will appear. 3. Enter a class name, add any fields you want. <img alt="1454" /> 4. Click **Create class button** to create a new class. <img alt="1690" /> ## Create records To create a single object, use the code snippet below. ```JavaScript JavaScript theme={null} const customObject = { className: 'RNCustomObject2', fields: { text: 'Lorem ipsum dolor sit amen', version: 7, tags: [ '#test', '#customobjects', '#quickblox-react-native-sdk'], friendsIds: [ 12345, 12346, 12347 ] } }; QB.objects .create(customObject) .then(function (cObject) { /* custom object created successfully */ }) .catch(function (e) { /* handle error */ }) ``` To create multiple objects, use the code snippet below. ```JavaScript JavaScript theme={null} const customObjects = { className: 'RNCustomObject2', objects: [{ tags: [ '#test', '#customobjects', '#quickblox-react-native-sdk'], text: 'Lorem ipsum dolor sit amen 1', version: 1500 }, { friendsIds: [], tags: [ '#test', '#customobjects', '#quickblox-react-native-sdk'], text: 'Lorem ipsum dolor sit amen 2', version: 1001 }, { tags: [ '#test', '#customobjects', '#quickblox-react-native-sdk'], text: 'Lorem ipsum dolor sit amen 3', version: 13 }] }; QB.objects .create(customObjects) .then(function (cObjects) { /* custom objects created successfully */ }) .catch(function (e) { /* handle error */ }) ``` ## Retrieve records by IDs To get records with a particular record ID, use the `getByIds()` method. Set the record ID using the `objectsIds` field of the `query` object. Go over [Sort operators](/sdks/react-native-custom-objects#sort-operators) and [Search operators](/sdks/react-native-custom-objects#search-operators) sections to learn about filters and search operators you can use to retrieve records. ```JavaScript JavaScript theme={null} const query = { className: 'RNCustomObject2', objectsIds: ['5d4175afa0eb4715cae5b63f'] }; QB.objects .getByIds(query) .then(function (results) { /* records found by IDs */ }) .catch(function (e) { /* handle error */ }) ``` The `getByIds()` method accepts one argument of the object type that has the following fields: | Fields | Required | Description | | ---------- | -------- | ------------------------------ | | className | yes | Name of a custom object class. | | objectsIds | yes | Custom objects IDs. | ## Retrieve records You can search for records of a particular class. The request below will return records of the `RNCustomObject2` class, with the `value` greater than `1000`, sorted by the `created_at` field in descending order. ```JavaScript JavaScript theme={null} const query = { className: 'RNCustomObject2', filter: { field: 'version', operator: QB.objects.OBJECTS_SEARCH_OPERATOR.FOR_TYPE.INTEGER.GTE, value: '1000' }, sort: { ascending: false, field: 'created_at' }, limit: 5, skip: 5 }; QB.objects .get(query) .then(function (results) { /* records found */ }) .catch(function (e) { /* handle error */ }) ``` The method `get()` method accepts one argument of the object type with the following fields: | Field | Required | Description | | --------- | -------- | ------------------------------------------------------------------------------------------- | | className | yes | A name of a custom object class. | | filter | no | Specifies filtering criteria for the field. | | sort | no | Specifies sorting criteria for the field. | | skip | no | Skip N records in search results. Useful for pagination. Default (if not specified): **0**. | | limit | no | Limit search results to N records. Useful for pagination. Default value: **100**. | If you want to retrieve only records updated after some specific date time and order the search results, you can apply operators. Thus, you can apply [search](/sdks/react-native-custom-objects#search-operators) and [sort](/sdks/react-native-custom-objects#sort-operators) operators to the list of records on the page so that it is easier to view specific records. ### Search operators You can use search operators to get more specific search results. The request below will return records of the `Appointment` class by the `priority` field with the value less than `1`, sorted in descending order by the `updated_at` field. ```JavaScript JavaScript theme={null} const query = { className: "Appointment", filter: { field: "priority", operator: QB.objects.OBJECTS_SEARCH_OPERATOR.FOR_TYPE.INTEGER.LT, value: "1", }, sort: { ascending: false, field: "updated_at", }, }; QB.objects .get(query) .then(function (results) { /* records found */ }) .catch(function (e) { /* handle error */ }); ``` Look at `QB.objects.OBJECTS_SEARCH_OPERATOR` object to find out what search operators you can use. It contains several operators separated by value type. Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Description | | ---------------- | ------------------------------- | ---------------------------------------------------- | | lt | integer, float | **Less Than** operator. | | lte | integer, float | **Less Than** or **Equal** to operator. | | gt | integer, float | **Greater Than** operator. | | gte | integer, float | **Greater Than** or **Equal** to operator. | | ne | integer, float, string, boolean | **Not Equal** to operator. | | in | integer, float, string | **IN** array operator. | | or | integer, float, string | All records that contain a value 1 **or** value 2. | | nin | integer, float, string | Not **IN** array operator. | | all | array | **ALL** are contained in array. | | ctn | string | All records that **contain** a particular substring. | ### Sort operators You can use sort operators to order the search results. The request below will return 10 records of the `Appointment` class sorted in descending by the `priority` field. ```JavaScript JavaScript theme={null} const query = { className: "Appointment", sort: { ascending: false, field: "priority", }, limit: 10, }; QB.objects .get(query) .then(function (results) { /* records found */ }) .catch(function (e) { /* handle error */ }); ``` Here are the sort options that you can use to order search results. | Sort options | Applicable to types | Description | | ------------ | ------------------- | ----------------------------------------------------------------------- | | ascending | All types | Sort results in the ascending order by setting the ascending as true. | | decsending | All types | Sort results in the descending order by setting the ascending as false. | ## Update records You can update a single record using the code snippet below. ```JavaScript JavaScript theme={null} const payload = { className: customObject.className, id: customObject.id, fields: { text: "Lorem ipsum dolor sit amen UPDATED", tags: { operator: QB.objects.OBJECTS_UPDATE_OPERATOR.FOR_TYPE.ARRAY.ADD_TO_SET, value: "#update", }, friendsIds: { operator: QB.objects.OBJECTS_UPDATE_OPERATOR.FOR_TYPE.ARRAY.PULL, pullFilter: QB.objects.PULL_FILTER.IN, value: currentUser.id, }, version: { operator: QB.objects.OBJECTS_UPDATE_OPERATOR.FOR_TYPE.INTEGER.INC, value: "1", }, }, }; QB.objects .update(payload) .then(function (updated) { /* custom object updated successfully */ }) .catch(function (e) { /* handle error */ }); ``` You can update multiple records using the code snippet below. ```JavaScript JavaScript theme={null} const payload = { className: object1.className, objects: [ { id: object1.id, fields: { text: `Lorem ipsum dolor sit amen UPDATED 123`, tags: ["#update"], }, }, { id: object2.id, fields: { friendsIds: [1001001, 1001010], version: 1250, }, }, ], }; QB.objects .update(payload) .then(function (updated) { /* items updated successfully */ }) .catch(function (e) { /* handle errror */ }); ``` ## Delete records To delete a single record, use the code snippet below. ```JavaScript JavaScript theme={null} const query = { className: object.className, ids: object.id, }; QB.objects .remove(query) .then(function () { /* removed successfully */ }) .catch(function (e) { /* handle error */ }); ``` To delete multiple records, use the code snippet below. ```JavaScript JavaScript theme={null} const query = { className: object1.className, ids: [object1.id, object2.id], }; QB.objects .remove(payload) .then(function () { /* removed successfully */ }) .catch(function (e) { /* handle error */ }); ``` ## Relations It is possible to create a relation between objects of **two different classes** via `_parent_id` field. For example, we have the class **Rating** that contains `score`, `review`, and `comment` fields. We also have a **Movie** class. So we can create a record of class **Rating** that will point to the record of the class **Movie** via its `_parent_id` field, so the `_parent_id` field will contain the ID of record from class **Movie**. <Warning> This is not a simple soft link. This is actually a **hard link**. When you delete the **Movie** class record then all its children (records of class **Rating** with `_parent_id` field set to the **Movie** class record ID) will be automatically **deleted** as well. </Warning> # Push Notifications Source: https://docs.quickblox.com/sdks/react-native-push-notifications Use this page to learn how to send push notifications to users when they are offline. Push Notifications provide a way to deliver some information to a user while they are not using your app actively. The following use cases can be covered by push notifications: * **Offline messages.** Send a chat message when a recipient is offline. In this case, a push notification will be sent automatically if the user is offline. * **Offline calls.** Make a video/audio call with offline opponents. In this case, a push notification will be sent manually. * **Requests to contact list.** Send requests to add a user to the contact list. In this case, a push notification will be sent manually). * **User tags.** Send notifications to specific user groups defined by tags. Learn more information from the [Push Notifications](/docs/key-concepts#push-notifications) section of our Key Concepts page. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/react-native-authentication) page to learn how to do it. ## Configuration To start working with push notifications, you need to install [react-native-push-notification](https://github.com/zo0r/react-native-push-notification) library. Follow the setup steps in the [README](https://github.com/zo0r/react-native-push-notification/blob/master/README.md) file. After that, follow the platform-specific steps on this page. <Note> Use the 3.2.1 version of the react-native-push-notification library to be able to display push notifications. </Note> ### iOS **Step1**. Create APNs certificate and upload it to the Dashboard. Each iOS application that uses Apple Push Notifications must have an APNs certificate. Upload the APNs certificate to QuickBlox **Dashboard => Push Notifications => Settings => Apple Push Notification Service (APNs)**. To learn how to create APNs certificates and upload them to Dashboard, refer to [our guide](/sdks/ios-push-notifications#create-apns-certificate). **Step2**. Open your project in Xcode and enable Push Notifications capabilities. 1. Open Xcode and choose your project file. 2. Choose the **Signing & Capabilities** tab. 3. Add a **Push Notifications** and **Background Modes** capabilities. To see these sections, you should be logged in to Xcode with your Apple ID that is enrolled in the Apple developer program. 4. Turn on a **Push Notifications** capability. 5. Turn on a **Background modes** capability. This section requires you to add specific app permissions. 6. Select a **Remote notifications** checkbox if you want to receive push notifications when the app goes to background mode. 7. Select a **Voice over IP** checkbox if you want your app to receive incoming Voice-over-IP (VoIP) push notifications and use them to display the system call interface to the user when the app is in the background mode. <Frame> <img alt="Set push notifications 1.jpg" /> </Frame> ### Android **Step 1**. Configure Firebase project and get **API key** and **Sender ID**. First, you should create a Firebase account if you don't have it. Then you should configure your [Firebase project](https://console.firebase.google.com/). To learn how to configure a Firebase project and get **API key** and **Sender ID**, refer to [our guide](/sdks/android-push-notifications#configure-firebase-project-and-api-key) . **Step 2**. Add Firebase to your project. 1. Prepare app dependencies. As part of enabling Firebase services in your Android application, you need to add the google-services plugin to your project **android/build.gradle** file. ```Groovy Groovy theme={null} dependencies { classpath 'com.google.gms:google-services:4.3.3' //... } ``` Add Firebase dependency and include **gms plugin** in the bottom of your module **android/app/build.gradle**. ```Groovy Groovy theme={null} implementation "com.google.firebase:firebase-analytics:17.3.0" //... apply plugin: 'com.google.gms.google-services' ``` <Warning> Make sure that `apply plugin: 'com.google.gms.google-services' ` is at the end of the file to avoid compilation errors. </Warning> 1. Put your **google-services.json** file for your package into **android/app/** folder. ## Initialize react-native-push-notification lib Initialize the **react-native-push-notification** lib using the code snippet below. ```JavaScript JavaScript theme={null} import PushNotificationIOS from "@react-native-community/push-notification-ios"; import PushNotification from 'react-native-push-notification'; // Must be outside of any component LifeCycle (such as `componentDidMount`). PushNotification.configure({ // (optional) Called when Token is generated (iOS and Android) onRegister: function (token) { console.log("TOKEN:", token); }, // (required) Called when a remote is received or opened, or local notification is opened onNotification: function (notification) { console.log("NOTIFICATION:", notification); // process the notification // (required) Called when a remote is received or opened, or local notification is opened notification.finish(PushNotificationIOS.FetchResult.NoData); }, // (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android) onAction: function (notification) { console.log("ACTION:", notification.action); console.log("NOTIFICATION:", notification); // process the action }, // (optional) Called when the user fails to register for remote notifications. Typically occurs when APNs is having issues, or the device is a simulator. (iOS) onRegistrationError: function (err) { console.error(err.message, err); }, // IOS ONLY (optional): default: all - Permissions to register. permissions: { alert: true, badge: true, sound: true, }, // Should the initial notification be popped automatically // default: true popInitialNotification: true, // * (optional) default: true // * - Specified if permissions (ios) and token (android and ios) will requested or not, // * - if not, you must call PushNotificationsHandler.requestPermissions() later // * - if you are not using remote notification or do not have Firebase installed, use this: // * requestPermissions: Platform.OS === 'ios' requestPermissions: true, }); ``` ## Subscribe to push notifications Subscribe to FCM or APNs service. Once the device is subscribed, the service delivers a notification to it. For **VoIP push notifications**, specify `push_channel: apns_voip` channel. ```JavaScript JavaScript theme={null} import { Platform } from 'react-native' //... onRegister: function(token) { // token obtained from APNs or FCM const config = Platform.OS === 'ios' ? { deviceToken: token, // to receive incoming call notification on iOS device(s) APNS_VOIP should be used pushChannel: QB.subscriptions.PUSH_CHANNEL.APNS_VOIP } : { deviceToken: token.token } QB.subscriptions .create(config) .then((subscriptions) => { /* subscription(s) created successfully */ }) .catch(e => { /* handle error */ }) } //... ``` The `create()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ----------- | -------- | ----------------------------------------- | | deviceToken | yes | Token received from FCM/APNs. | | pushChannel | no | Channel for receiving push notifications. | <Warning> The `deviceToken` is a device registration token generated by the APNs or GCM/FCM. The token can be unregistered by the APNs or GCM/FCM anytime. In this case, the device should be registered again and obtain a new token. When a new token is obtained, a new subscription should be created. </Warning> ## Send push notifications You can initiate sending a push notification to a user on any event in your application. To do so, you need to set push notification parameters, push recipients, notification type, sender ID, and type of the notification event. For **VoIP push notifications**, you need to set the `ios_voip: 1` parameter. ```JavaScript JavaScript theme={null} const event = { notificationType: QB.events.NOTIFICATION_TYPE.PUSH, payload: { ios_voip: 1, // to send VoIP push notification (https://docs.quickblox.comhttps://docs.quickblox.com/reference/push-notifications#push-notification-formats) message: "Your push notification message", // key: value }, recipientsIds: [51,46], // users' IDs to deliver notification senderId: 45, // ID of the user who created the event type: QB.events.NOTIFICATION_EVENT_TYPE.ONE_SHOT, }; QB.events .create(event) .then(function (data) { /* notification event(s) created successfully */ }) .catch(function (e) { /* handle error */ }); ``` The `create()` method accepts one argument of the object type that has the following fields: | Parameter | Required | Description | | ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | notificationType | yes | Type of notifications:- `QB.events.NOTIFICATION\_TYPE.PUSH` - send push notification.- `QB.events.NOTIFICATION\_TYPE.EMAIL` - send email. | | payload | yes | Push notification payload. Can contain a message (string) property and many other key-value pairs (string-string). Refer [here](https://docs.quickblox.com/reference/push-notifications#push-notification-formats) for more details. | | recipientsIds | no | Users' IDs to deliver notification. | | senderId | yes | ID of the user who created the event. | | type | yes | Types of the notification event.- `QB.events.NOTIFICATION\_EVENT\_TYPE.ONE\_SHOT` - a one-time event (valid only if the date is not specified).- `QB.events.NOTIFICATION\_EVENT\_TYPE.FIXED\_DATE` - a one-time event that occurs at a specified date (valid only if the date is specified).- `QB.events.NOTIFICATION\_EVENT\_TYPE.PERIOD\_DATE` - is a reusable event that occurs within a given period from the initial date (valid only if the period is specified). | <Warning> You can send only FCM data messages to the Android app. QuickBlox doesn't support FCM notification messages. To process FCM data messages on your app when the app is in the background, you need to handle them. If not handled, they will not pop on the screen even if the app has received such push notification. See [FCM documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#notifications%5Fand%5Fdata%5Fmessages) to learn more about data messages. </Warning> <Note> How to set up the FCM console and plug into QuickBlox dash board please see [How to enable Cloud Messaging API (Legacy)](/sdks/android-push-notifications#how-to-enable-cloud-messaging-api-legacy) or [HTTP v1 API](/sdks/android-push-notifications#http-v1-api) </Note> <Tip> You can send APNs VoIP notifications to the iOS app. However, if the iOS app is not subscribed to APNs VoIP notifications or the APNs VoIP certificate has expired, the regular APNs will be delivered instead of APNs VoIP. </Tip> ## Receive push notifications When any notification is opened or received the `onNotification` callback is called passing an object with the notification data. ```JavaScript JavaScript theme={null} // (required) Called when a remote is received or opened, or local notification is opened onNotification: function (notification) { console.log("NOTIFICATION:", notification); // process the notification // (required) Called when a remote is received or opened, or local notification is opened notification.finish(PushNotificationIOS.FetchResult.NoData); }, ``` Let’s review the notification object below: ```JavaScript JavaScript theme={null} { foreground: false, // BOOLEAN: If the notification was received in foreground or not userInteraction: false, // BOOLEAN: If the notification was opened by the user from the notification area or not message: 'My Notification Message', // STRING: The notification message data: {}, // OBJECT: The push data } ``` ## Unsubscribe from push notifications You can unsubscribe from receiving push notification by passing a subscription ID. ```JavaScript JavaScript theme={null} const subscriptionsRemoveParams = { id: "your subscription Id" }; QB.subscriptions .remove(subscriptionsRemoveParams) .then(() => { /* removed successfully */ }) .catch(function (e) { /* handle error */ }); ``` ## CallKit and VoIP push notifications If you need to display a native calling interface for incoming and outgoing calls, you should use CallKit on iOS. It lets you integrate your calling services with other call-related apps on the system. Use [Apple CallKit](https://developer.apple.com/documentation/callkit) guide to learn how to integrate a CallKit functionality. For iOS, together with the CallKit, you also need to integrate [VoIP push notifications](/sdks/ios-push-notifications#subscribe-for-voip-push-notification). QuickBlox supports iOS VoIP push notifications via the same API described above: 1. For VoIP pushes, you need to generate a separated VoIP device token. See how to get it [here](https://github.com/react-native-webrtc/react-native-voip-push-notification#usage). 2. When the token is retrieved, you need to subscribe to VoIP push notifications by passing a `push_channel: apns_voip` in the [subscription request](/sdks/react-native-push-notifications#subscribe-to-push-notifications). 3. To [send a VoIP push notification](/sdks/react-native-push-notifications#send-push-notifications), use `ios_voip: 1` parameter in a push payload of the create event request. ## Troubleshooting #### A subscription is removed after a push is sent and the push isn't delivered **Cause**: a device registration token is invalid. <Note> The device registration token is represented as `deviceToken` within the system. See [this section](/sdks/react-native-push-notifications#subscribe-to-push-notifications) to learn how to subscribe a device to push notifications. </Note> **Tip**: check if the device registration is correct. The device registration token can be invalid due to a number of reasons: 1. Some other data is set instead of a correct device registration token. For example, a Firebase project ID, Firebase user token, etc. 2. The client app unregistered itself from GCM/FCM. This can happen if the user uninstalls the application or, on iOS, if the APNs Feedback Service reported the APNs token as invalid. 3. The registration token expired. For example, Google might decide to refresh registration tokens or the APNs token may have expired for iOS devices. 4. The client app was updated, but the new version is not configured to receive messages. For all these cases, remove the invalid device registration token and stop using it to send messages. Then, obtain a new token and make sure to create a new subscription with a valid token. # Quick Start Source: https://docs.quickblox.com/sdks/react-native-quick-start Learn how to install QuickBlox SDK and send your first message. QuickBlox SDK helps you implement real-time chat, video chat, and push notifications to your app. You can fully concentrate on your mobile app development. ## Start with sample apps Choose the code sample below to jump-start the development. We use GitHub repositories to make it easy to explore, copy, and modify our code samples. The guide on how to launch and configure the sample app is on GitHub. <CardGroup> <Card title="React Native Chat Sample App" icon="react"> <a href="https://github.com/QuickBlox/quickblox-react-native-samples/tree/master/chat-sample"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/react-native-chat"> <Icon icon="book" /> Documentation </a> </Card> <Card title="React Native Video Calling Sample App" icon="react"> <a href="https://github.com/QuickBlox/quickblox-react-native-samples/tree/master/webrtc-sample"> <Icon icon="github" /> View on GitHub </a> <br /> <a href="/sdks/react-native-video-calling"> <Icon icon="book" /> Documentation </a> </Card> </CardGroup> For more samples, head to our [Code Samples](/code-samples/code-samples) page. These sample apps are available on GitHub so feel free to browse them there. ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app by clicking the **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click **Add** button. 4. Go to **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** . ## Requirements The minimum requirements for QuickBlox React Native SDK are: * iOS 12.0 * Android (minimum version 5.0, API 21) * React Native (minimum version 0.60) ## Install QuickBlox SDK into your app <Note> To manage project dependencies: * [Node.js](https://nodejs.org/en/) and [npm](https://docs.npmjs.com/getting-started) must be installed * [CocoaPods](https://cocoapods.org) must be installed for iOS. </Note> To connect QuickBlox to your app just add it into your project's dependencies by running following code sample in terminal (in your react-native app): ```Bash Bash theme={null} npm install quickblox-react-native-sdk ``` iOS and Android have different dependencies systems. For that reason, you need to install dependencies in your iOS project. Navigate to **ios/** folder in the root directory of the project and enter the following code snippet. ```Bash Bash theme={null} pod install ``` <Warning> Make sure that iOS version in your project’s **Podfile** is **not lower than** 12.0 when installing SDK. Otherwise, CocoaPods will fail to find a compatible version of **QuickBlox React Native SDK**. </Warning> #### ⚠️ Android – Breaking Change (from v0.12.0 and above) Starting from **v0.12.0**, the SDK **no longer manages permissions** required for calling functionality on Android. If your app uses calling features, you must **manually declare** the necessary permissions in your app’s `AndroidManifest.xml` file. <Note> Kindly ensure you're modifying the main `AndroidManifest.xml` file located at the application level — not a test or variant manifest. </Note> ```xml XML theme={null} <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <!-- Permissions required for QuickBlox calling functionality --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" /> <!-- other manifest entries --> </manifest> ``` ## Send your first message ### Initialize QuickBlox SDK Initialize the SDK with your application credentials. Set the `appId`, `authKey`, `authSecret`, `accountKey` properties of the `appSettings` object. Call the `init()` method and pass the `appSettings` as an argument to it. ```JavaScript JavaScript theme={null} const appSettings = { appId: '76730', authKey: 'XydaWcf8OO9xhGT', authSecret: 'iiohfdija792hjt', accountKey: '7yvNe17TnjNUqDoPwfqp', }; QB.settings .init(appSettings) .then(function () { // SDK initialized successfully }) .catch(function (e) { // Some error occurred, look at the exception message for more details }); ``` <Warning> You must initialize SDK before calling any methods through the SDK, except for the `init()` method. If you attempt to call a method without initializing SDK previously, the error will be returned. </Warning> <Warning> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend and initialize QuickBlox SDK with applicationId and acountKey only. More details you can find in [Initialize QuickBlox SDK without Authorization Key and Secret](/sdks/react-native-setup#initialize-quickblox-sdk-without-authorization-key-and-secret) section. </Warning> ### Authorize user Now, it is time to log in with the user. To get it done, set the `login` and `password` properties of the `loginParams` object. Call the `login()` method and pass the `loginParams` as an argument to it using the code snippet below. ```JavaScript JavaScript theme={null} const loginParams = { login: 'yourlogin', password: 'yourpassword' }; QB.auth .login(loginParams) .then(function (info) { // signed in successfully, handle info as necessary // info.user - user information // info.session - current session }) .catch(function (e) { // handle error }); ``` ### Connect to chat Having authorized a user, you can proceed with connecting to the chat server to start using Chat module functionality. Set the `userId` and `password` properties of the `connectParams` object. Call the `connect()` method and pass the `connectParams` as an argument to it. ```JavaScript JavaScript theme={null} const connectParams = { userId: 12345, password: 'passw0rd!' }; QB.chat .connect(connectParams) .then(function () { // connected successfully }) .catch(function (e) { // some error occurred }); ``` ### Create dialog QuickBlox provides three types of dialogs: **1-1 dialog**, **group dialog**, and **public dialog**. Learn more about dialogs [here](/sdks/react-native-chat-dialogs#create-dialog). Let’s create a simple **1-1 dialog**. Set the `type` and `occupantsIds` properties of the `createDialogParams` object. Call the `createDialog()` method and pass the `createDialogParams` as an argument to it. ```JavaScript JavaScript theme={null} const createDialogParams = { type: QB.chat.DIALOG_TYPE.CHAT, occupantsIds: [12345] }; QB.chat .createDialog(createDialogParams) .then(function (dialog) { // handle as necessary, i.e. // subscribe to chat events, typing events, etc. }) .catch(function (e) { // handle error }); ``` ### Subscribe to receive messages QuickBlox SDK emits events to notify about chat events. Thus, when a message has been received, a user receives the event from SDK about a new incoming message. To process events, you need to provide an event handler that SDK will call. See the code snippet below. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from 'react-native' const eventHandler = (event) => { const { type, payload } = event // type - type of the event (string) // payload - new message (object) } const emitter = new NativeEventEmitter(QB.chat) emitter.addListener( QB.chat.EVENT_TYPE.MESSAGE.RECEIVED_NEW_MESSAGE, eventHandler ) ``` ### Send message To send a message, set the `dialogId` and `body` properties of the `message` object. Call the `sendMessage()` method and pass the `message` as an argument. ```JavaScript JavaScript theme={null} const message = { dialogId: 'dsfsd934329hjhkda98793j2', body: 'Hey there!', saveToHistory: true }; QB.chat .sendMessage(message) .then(function () { /* send successfully */ }) .catch(function (e) { /* handle error */ }) ``` <Note> Set the `saveToHistory` parameter if you want this message to be saved in chat history. </Note> # Setup Source: https://docs.quickblox.com/sdks/react-native-setup Learn how to add and configure QuickBlox SDK for your app. Follow the instructions below to ensure that QuickBlox SDK runs smoothly with your app. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Get application credentials QuickBlox application includes everything that brings messaging right into your application - chat, video calling, users, push notifications, etc. To create a QuickBlox application, follow the steps below: 1. Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. 2. Create the app by clicking the **New app** button. 3. Configure the app. Type in the information about your organization into corresponding fields and click **Add** button. 4. Go to **Dashboard => *YOUR\_APP* => Overview** section and copy your **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** . ## Requirements The minimum requirements for QuickBlox React Native SDK are: * iOS 12.0 * Android (minimum version 5.0, API 21) * React Native (minimum version 0.60) ## Install QuickBlox SDK into your app <Note> To manage project dependencies: * [Node.js](https://nodejs.org/en/) and [npm](https://docs.npmjs.com/getting-started) must be installed * [CocoaPods](https://cocoapods.org) must be installed for iOS. </Note> To connect QuickBlox to your app just add it into your project's dependencies by running following code sample in terminal (in your react-native app): ```Bash Bash theme={null} npm install quickblox-react-native-sdk ``` iOS and Android have different dependencies systems. For that reason, you need to install dependencies in your iOS project. Navigate to **ios/** folder in the root directory of the project and enter the following code snippet. ```Bash Bash theme={null} pod install ``` ## Add permissions ### For Android Starting from **v0.12.0**, the SDK **no longer manages permissions** required for calling functionality on Android. This change provides developers with more control over their app's permission handling. If your app uses calling features, please make sure to **manually declare** the required permissions in your app’s `AndroidManifest.xml` file. Additionally, if your app requires access to the camera, microphone, internet, or storage, you may need to include related permissions in the manifest as well. <Note> Kindly ensure you're modifying the main `AndroidManifest.xml` file located at the application level — not a test or variant manifest. </Note> ```XML XML theme={null} <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <!-- Permissions required for QuickBlox calling functionality --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" /> <!-- Common permissions for accessing camera, microphone, network, and storage --> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> </manifest> ``` <Note> Note that mentioning the camera and microphone permissions in the manifest isn't always enough. You need to request camera and microphone permissions additionally at runtime. </Note> ### For iOS You can use our SDK in the background mode as well. If you want to receive push notifications when the app goes to background mode, this requires you to add specific app permissions. Under the app build settings, open the **Capabilities** tab and turn on **Remote notifications** checkbox only. <Frame> <img alt="react-native-add-remote-notifications-permission" /> </Frame> If you want to use video calling functionality in the background mode, set the **Audio, AirPlay, and Picture in Picture** checkboxes. <Frame> <img alt="react-native-audio-airplay-permission" /> </Frame> ## Initialize QuickBlox SDK <Warning> You must initialize SDK before calling any methods through the SDK, except for the `init()` method. If you attempt to call a method without initializing SDK previously - the error will be returned. </Warning> Initialize the framework with your application credentials. Specify **Application ID**, **Authorization Key**, **Authorization Secret**, and **Account Key** within `appSettings` object and pass it to the `init()` method. ```JavaScript JavaScript theme={null} const appSettings = { appId: '', authKey: '', authSecret: '', accountKey: '' }; QB.settings .init(appSettings) .then(function () { // SDK initialized successfully }) .catch(function (e) { // Some error occurred, look at the exception message for more details }); ``` <Warning> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend. </Warning> The `init()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------- | | appId | yes | Application ID. | | authKey | yes | Authorization key. | | authSecret | yes | Authorization secret. | | accountKey | yes | Required to get actual apiEndpoint and chatEndpoint for the right server. | ## Initialize QuickBlox SDK without Authorization Key and Secret You may don't want to store **authKey** and **authSecret** inside an application for security reasons. In such case, you can initialize QuickBlox SDK with **applicationId** and **accountKey** only, and store your **authKey** and **authSecret** on your backend. But, if so, the implementation of [authentication with QuickBlox](https://docs.quickblox.com/reference/authentication) should be also moved to your backend. ```JavaScript JavaScript theme={null} const appSettings = { appId: '', accountKey: '' }; QB.settings .init(appSettings) .then(function () { // SDK initialized successfully }) .catch(function (e) { // Some error occurred, look at the exception message for more details }); ``` Then using your backend you can authorize a user in the QuickBlox system, send back the user session token, and set it to the QuickBlox SDK using `startSessionWithToken()` method. You can find out more about this in the [Set existing session](/sdks/react-native-authentication#set-existing-session) section. ## Point SDK to enterprise server To point QuickBlox SDK to the QuickBlox enterprise server, you should set `apiEndpoint` and `chatEndpoint` fields of the `appSettings` object and pass it to the `init()` method. ```JavaScript JavaScript theme={null} const appSettings = { appId: '', authKey: '', authSecret: '', accountKey: '', apiEndpoint: '', // optional chatEndpoint: '' // optional }; QB.settings .init(appSettings) .then(function () { // SDK initialized successfully }) .catch(function (e) { // Some error occurred, look at the exception message for more details }); ``` The `init()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ------------ | -------- | ------------------------------------------------------------------------- | | appId | yes | Application ID. | | authKey | yes | Authorization key. | | authSecret | yes | Authorization secret. | | accountKey | yes | Required to get actual apiEndpoint and chatEndpoint for the right server. | | apiEndpoint | no | API endpoint. | | chatEndpoint | no | Chat endpoint. | <Note> [Contact our sales team](https://quickblox.com/enterprise/#get) to get **API endpoint** and **chat endpoint**. </Note> ## Enable auto-reconnect to Chat QuickBlox Chat runs over XMPP protocol. To receive messages in a real-time mode, the application should be connected to the Chat over XMPP protocol. To enable auto-reconnect to Chat, call `enableAutoReconnect()` method and pass `enable` as `true`. ```JavaScript JavaScript theme={null} const autoReconnectParams = { enable: true }; QB.settings .enableAutoReconnect(autoReconnectParams) .then(function () { // operation completed successfully }) .catch(function (e) { // Some error occurred, look at the exception message for more details }); ``` Thus, if XMPP connection is lost and autoreconnect functionality is enabled, the app connects to Chat automatically. You can disable autoreconnection to Chat by passing `enable` parameter as `false` to `enableAutoReconnect()` method. ```JavaScript JavaScript theme={null} const autoReconnectParams = { enable: false }; QB.settings .enableAutoReconnect(autoReconnectParams) .then(function () { // operation completed successfully }) .catch(function (e) { // Some error occurred, look at the exception message for more details }); ``` <Note> **By default**, this parameter is enabled. Set autoreconnection before calling the `login()` method so it could be applied in a current chat. </Note> ## Enable logging Logging functionality allows you to keep track of all events and activities while running your app. As a result, you can monitor the operation of the SDK and improve the debug efficiency. There are 3 logging use cases: * **Server API** logging is used to monitor Server API calls. * **Chat** logging is used to monitor chat issues. * **WebRTC** logging is used to gather issues with video. 1. Go over [this section](/sdks/ios-setup#enable-logging) to learn how to enable logging for your iOS app. 2. Go over [this section](/sdks/android-setup#enable-logging) to learn how to enable logging for your Android app. ## Message carbons Message carbons functionality allows for multi-device support. Thus, all user messages get copied to all their devices so they could keep up with the current state of the conversation. For example, a User A has phone running conversations and desktop running conversations. User B has desktop running conversations. When User B sends a message to User A, the message shows on both the desktop and phone of User A. **Enable message carbons** ```JavaScript JavaScript theme={null} QB.settings .enableCarbons() .then(function () { // operation completed successfully }) .catch(function (e) { // Some error occurred, look at the exception message for more details }); ``` **Disable message carbons** ```JavaScript JavaScript theme={null} QB.settings .disableCarbons() .then(function () { // operation completed successfully }) .catch(function (e) { // Some error occurred, look at the exception message for more details }); ``` <Warning> **By default**, this parameter is turned off. Keep it enabled if you want to receive messages on all devices of the particular user (when a user is logged in on several devices). </Warning> ## Stream management Stream management has two important features **Stanza Acknowledgements** and **Stream Resumption**: * **Stanza Acknowledgements** is the ability to know if a stanza or series of stanzas has been received by one's peer. In other words, a reply is requested on every sent message. If the reply is received, the message is considered as delivered. * **Stream Resumption** is the ability to quickly resume a stream that has been terminated. Once a connection is re-established, Stream Resumption is executed. By matching the sequence numbers assigned to each Stanza Acknowledgement a server and client can verify which messages are missing and request to resend missing messages. Call `initStreamManagement()` method and pass `autoReconnect` and `messageTimeout` parameters to it to enable stream management. ```JavaScript JavaScript theme={null} const streamManagementParams = { autoReconnect: false, messageTimeout: 10 }; QB.settings .initStreamManagement() .then(function () { // operation completed successfully }) .catch(function (e) { // Some error occurred, look at the exception message for more details }); ``` The `initStreamManagement()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- | | autoReconnect | yes | Set it as true to use Stream resumption for new connections. Default: **false**. | | messageTimeout | yes | Preferred resumption time (in seconds). If this parameter is greater than 0, than it is applied, otherwise it is not applied. | <Note> You should enable Stream Management before you do the `login()` because the Stream Management is initialized while Chat login is performed. The Stream Management defines an extension for active management of a stream between a client and server, including features for stanza acknowledgments. </Note> # Users Source: https://docs.quickblox.com/sdks/react-native-users Learn how to manage your users with QuickBlox. Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/react-native-authentication) page to learn how to do it. ## Create user It's recommended to manage user creation at your backend for production. To learn more you can refer to [QuickBlox API documentation](https://docs.quickblox.com/reference/create-user). For POCs/MVPs or during development you may want to create users on the fly, you can use `create()` method. Create a user using the code snippet below. Only login (or email) and password are required. Other fields are optional. ```JavaScript JavaScript theme={null} const createUserParams = { email: '[[email protected]](/cdn-cgi/l/email-protection)', fullName: 'Jack Sparrow', login: 'jack', password: 'jackpassword', phone: '404-388-5366', tags: ['awesome', 'quickblox'] }; QB.users .create(createUserParams) .then(function (user) { // user created successfully }) .catch(function (e) { // handle as necessary }); ``` <Warning> **Security & Privacy** It's recommended to [disable permission](/docs/application#set-session-permissions) to create users with application session on **production apps** once user creation is implemented on your backend. Email, full name, facebookId and phone number are PII, [configure session permissions](/docs/application#set-session-permissions) according to your privacy requirements. </Warning> ## Retrieve users Get a list of users using the `getUsers()` method below. The code snippet below shows how to get a list of users with `full_name` containing `John`, sorted by the `updated_at` field in descending order, and limited to 10 users on the page. ```JavaScript JavaScript theme={null} // * @param {Object} result // * @param {QBUser[]} result.users Array of users returned from API request // * @param {number} result.page Page of results // * @param {number} result.perPage How much items returned per page // * @param {number} result.total Total amount of results function processUsers(result) { // } const filter = { field: QB.users.USERS_FILTER.FIELD.FULL_NAME, type: QB.users.USERS_FILTER.TYPE.STRING, operator: QB.users.USERS_FILTER.OPERATOR.IN, value: 'John' }; const sort = { ascending: false, field: QB.users.USERS_SORT.FIELD.LAST_REQUEST_AT, type: QB.users.USERS_SORT.TYPE.STRING }; const getUsersQuery = { filter: filter, sort: sort, page: 1, perPage: 10 }; QB.users .getUsers(getUsersQuery) .then(processUsers) .catch(function (error) { // handle error }); ``` The method `getUsers()` accepts one (optional) argument of the object type that has the following fields: | Field | Required | Desription | | ------- | -------- | -------------------------------------------- | | filter | no | Specifies filtering criteria for the field. | | sort | no | Specifies sorting criteria for the field. | | page | no | Number of pages with results to be returned. | | perPage | no | Number of records to return in one page. | If you want to retrieve only users updated after some specific date time, you can use operators. This is useful if you cache users somehow and do not want to obtain the whole list of your users on every app start. Thus, you can use [search operators](/sdks/react-native-users#search-operators) and [sort operators](/sdks/react-native-users#sort-operators) to list users on the page so that it is easier to see specific users. ### Search operators You can use search operators to get more specific search results. The request below will return users filtered by IDs. ```JavaScript JavaScript theme={null} const occupantsIds = dialog.occupantsIds; const filter = { field: QB.users.USERS_FILTER.FIELD.ID, type: QB.users.USERS_FILTER.TYPE.NUMBER, operator: QB.users.USERS_FILTER.OPERATOR.IN, value: occupantsIds.join() // value should be of type String }; const getUsersQuery = { filter: filter }; function processUsers(result) {} QB.users .getUsers(getUsersQuery) .then(processUsers) .catch(function (e) { // handle error }); ``` Here are the search operators that you can use to search for the exact data that you need. | Search operators | Applicable to types | Applicable to fields | Description | | ---------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ | | lt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less Than** operator. | | ge | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** or **Equal** to operator. | | gt | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Greater Than** operator. | | le | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Less or Equal to** operator | | eq | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Equal** to operator. | | ne | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Not Equal** to operator. | | between | number, string, date | id, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **Contained between values** operator. | | in | number, string, date | id, full\_name, email, login, phone, created\_at, updated\_at, last\_request\_at, external\_user\_id, facebook\_id | **IN** array operator. | ### Sort operators You can use sort operators to order the search results. The request below will return users sorted in descending order by the last date a user was requested. ```JavaScript JavaScript theme={null} const sort = { ascending: false, field: QB.users.USERS_SORT.FIELD.LAST_REQUEST_AT, type: QB.users.USERS_SORT.TYPE.STRING }; const getUsersQuery = { sort: sort }; function processUsers(result) {} QB.users .getUsers(getUsersQuery) .then(processUsers) .catch(function (e) { // handle error }); ``` Here are the sort options that you can use to order the search results: | Sort options | Applicable to types | Applicable to fields | Description | | ------------ | -------------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | ascending | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Sort results in the ascending order by setting the ascending as true. | | descending | All types | id, full\_name, email, login, phone, website, created\_at, updated\_at,last\_request\_at, external\_user\_id | Sort results in the ascending order by setting the ascending as false. | | field | String, Number, Date | id, full\_name, email, login, phone, website, created\_at, updated\_at, last\_request\_at, external\_user\_id | Sort Field Value | | type | String, Number, Date | id, full\_name, email, login, phone, website, created\_at, updated\_at, last\_request\_at, external\_user\_id | Type of Sort Field Value | ## Reset user password <Note> This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://help.quickblox.com/). </Note> ## Update user Update a user profile by calling the `update()` method. If you want to change your password, you need to provide 2 parameters: `password` and `newPassword`. As a result, the updated user entity will be returned. You can update any other field of the user using the `updateUser()` method. Thus, the snippet below shows how to update a `tagList` and `customData` fields. ```JavaScript JavaScript theme={null} const updateUserParams = { customData: JSON.stringify({ name: 'John', age: 31, city: 'New York' }), tags: ['testOne', 'tagTwo'] }; QB.users .update(updateUserParams) .then(function (updatedUser) { // update local user information with the data server returned }) .catch(function (e) { // handle error }); ``` The `update()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | customData | no | User custom data. Should be a String. You can convert any data types to String. For example, JSON, XML, etc. | | tags | no | User tags. An array of Strings. A tag must include alphanumeric characters only and start with a letter. There are no spaces in the tag format. For example, the "tagOne" format is correct while the "tag one" format is incorrect. The maximum number of tags is 10. If more that 10 tags are provided, an error is returned: Error: tag list should contain maximum 10 tags | ## Set user avatar To set a user avatar, just upload a file to the QuickBlox cloud storage and connect it to the user. To upload the file to the QuickBlox cloud storage, call the `upload()` method and pass the `fileUrl` to it. The `fileUrl` is a path to the file in the device filesystem. Now that the file is uploaded, get the ID of the uploaded file. To connect the file to the user, set the ID of the uploaded file to the `blobId` field of the `user` and call the `update()` method. As a result, the user avatar gets updated. ```JavaScript JavaScript theme={null} const fileUrl = "path to file in device's filesystem"; const contentUploadParams = { url: fileUrl, public: false }; QB.content .upload(contentUploadParams) .then(function (file) { // file uploaded successfully const updateUserParams = { blobId: file.id }; return QB.users.update(updateUserParams); }) .then(function (user) { // user updated successfully }) .catch(function (error) { // inspect error message to check what is wrong }); ``` The `update()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ------ | -------- | ------------- | | blobId | no | User blob ID. | ## Get user avatar You can also get access to the uploaded avatar of the user by calling the `getPrivateURL()` method. As a result, you will receive a private URL in the response. See [this section](/sdks/react-native-content) to learn more about file URLs and `getInfo()` method. ```JavaScript JavaScript theme={null} const contentGetInfoParams = { id: user.blobId }; // get access to the avatar that was set in user's "blobId" property QB.content .getInfo(contentGetInfoParams) .then(function (file) { const contentGetPrivateUrlParams = { uid: file.uid }; return QB.content.getPrivateUrl(contentGetPrivateUrlParams); }) .then(function (imageUrl) { // <Image source={{ uri: imageUrl }}/> }) .catch(function (error) { // inspect error message to check what is wrong }); ``` The `getPrivateUrl()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ----- | -------- | ------------------------- | | uid | yes | A file unique indetifier. | # Basic Source: https://docs.quickblox.com/sdks/react-native-video-calling Learn how to add peer-to-peer video calls to your app. QuickBlox Video Calling API is built on top of [WebRTC](https://webrtc.org/). It allows adding real-time video communication features into your app similar to Skype using API easily. The communication is happening between peers representing camera devices. There are two **peer types**: * **Local peer** is a device running the app right now. * **Remote peer** is an opponent device. Establishing real-time video communication between two peers involves 3 phases: 1. **Signaling**. At this phase, the peers’ local IPs and ports where they can be reached (ICE candidates) are exchanged as well as their media capabilities and call session control messages. 2. **Discovery**. At this phase, the public IPs and ports at which endpoints can be reached are discovered by STUN/TURN server. 3. **Establishing a connection**. At this phase, the data are sent directly to each party of the communication process. <Warning> In order to start using Video Calling Module, you need to **connect to QuickBlox Chat first**. The signaling in the QuickBox WebRTC module is implemented over the XMPP protocol using QuickBlox Chat Module. It acts as a signaling transport for Video Calling API. </Warning> <Note> Please use this WebRTC Video Calling to make the Group Calls with **4 or fewer users**. Because of [Mesh architecture](https://webrtcglossary.com/mesh/) we use for multi-point where every participant sends and receives its media to all other participants, the current solution supports group calls with up to 4 people. </Note> Visit [Key Concepts](/docs/key-concepts) page to learn the most important QuickBlox concepts. ## Before you begin 1. Register a [QuickBlox account](https://admin.quickblox.com/signin). This is a matter of a few minutes and you will be able to use this account to build your apps. 2. Configure QuickBlox SDK for your app. Check out [Setup](/sdks/react-native-setup) page for more details. 3. Create a user session to be able to use QuickBlox functionality. See [Authentication](/sdks/react-native-authentication) page to learn how to do it. 4. Connect to the Chat server to provide a signaling mechanism for Video Calling API. Follow our [Chat](/sdks/react-native-chat) page to learn about chat connection settings and configuration. ## Initialize WebRTC WebRTC module allows to process calls. When a call is initiated or received, a call session is created. If the module is not initialized, it will not be able to create the call session and process calls consequently. To initialize WebRTC module call `init()` method. ```JavaScript JavaScript theme={null} QB.webrtc .init() .then(function () { /* module is ready for calls processing */ }) .catch(function (e) { /* handle error */ }) ``` <Note> If you miss calling `init()` method, a `Promise.reject(error)` is returned: `The call service is not connected`. </Note> ## Manage calls <Warning> Each WebRTC session is assigned a unique session identifier (`sessionId`). You can get the `sessionId` from the `session` value returned from `call()`, `accept()`, `reject()`, `hangUp()` methods or from any event emitted by `QB.webrtc` module. See a full list of events [here](/sdks/react-native-video-calling#event-handler). </Warning> To process events such as **incoming call**, you need to add an event handler. Learn more details about the event handler configuration in the [Event handler](#event-handler) section. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from 'react-native' import QB from 'quickblox-react-native-sdk' function eventHandler (event) { const { type, // type of the event (i.e. `@QB/CALL` or `@QB/REJECT`) payload } = event const { userId, // id of QuickBlox user who initiated this event (if any) session // current or new session } = payload // handle as necessary } const emitter = new NativeEventEmitter(QB.webrtc) Object.keys(QB.webrtc.EVENT_TYPE).forEach(key => { emitter.addListener(QB.webrtc.EVENT_TYPE[key], eventHandler) }) ``` <Note> The example above shows assigning one handler for all module events but you can assign separate handlers for each event. </Note> ## Initiate a call To call users, you should create a call session and start calling using `call()` method. ```JavaScript JavaScript theme={null} import QB from 'quickblox-react-native-sdk' //... const params = { opponentsIds: [12345, 12346], type: QB.webrtc.RTC_SESSION_TYPE.VIDEO } QB.webrtc .call(params) .then(function (session) { /* session created */ }) .catch(function (e) { /* handle error */ }) ``` The `call()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | ------------ | -------- | ---------------------------------------------------------------------------------- | | opponentsIds | yes | IDs of the opponents. | | type | yes | Call type: QB.webrtc.RTC\_SESSION\_TYPE.VIDEO, QB.webrtc.RTC\_SESSION\_TYPE.AUDIO. | <Note> **Note** After this, your opponents will receive the `QB.webrtc.EVENT_TYPE.CALL` event. </Note> ## Accept a call To accept a call request, call `accept()` method and pass `sessionId` to tell SDK which call session to accept. ```JavaScript JavaScript theme={null} const acceptParams = { sessionId, userInfo: { // custom data can be passed using this object // only [string]: string type supported } }; QB.webrtc .accept(acceptParams) .then(function (session) { /* handle session */ }) .catch(function (e) { /* handle error */ }) ``` The `accept()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | --------- | -------- | ------------------------ | | sessionId | yes | Call session identifier. | | userInfo | no | Custom user data. | ## Reject a call To reject a call request, use `reject()` method and pass `sessionId` parameter to tell SDK which call session to reject. ```JavaScript JavaScript theme={null} const rejectParams = { sessionId, userInfo: { // custom data can be passed using this object // only [string]: string type supported } }; QB.webrtc .reject(rejectParams) .then(function (session) { /* handle session */ }) .catch(function (e) { /* handle error */ }) ``` The `reject()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | --------- | -------- | ------------------------ | | sessionId | yes | Call session identifier. | | userInfo | no | Custom user data. | ## End a call To end a call, use `hangUp()` method and pass `sessionId` parameter to tell SDK which call session to end. ```JavaScript JavaScript theme={null} const hangUpParams = { sessionId, userInfo: { // custom data can be passed using this object // only [string]: string type supported } }; QB.webrtc .hangUp(hangUpParams) .then(function (session) { /* handle session */ }) .catch(function (e) { /* handle error */ }) ``` The `hangUp()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | --------- | -------- | ------------------------ | | sessionId | yes | Call session identifier. | | userInfo | no | Custom user data. | ## Release resource When you do not want to receive and process video calls, for example, when a user is logged out, you have to release `QB.webrtc` module. Call `release()` method that allows to unregister `QB.webrtc` module from receiving any video chat events and closes existing signaling channels. ```JavaScript JavaScript theme={null} QB.webrtc .release() .then(() => { /* released successfully */ }) .catch(e => { /* handle error */ }) ``` <Note> `release()` method should be called when a video track is no more valid. If you do not call this method, you will get a memory leak. </Note> ## Local/remote video view Set up two video chat layouts for remote and local video tracks to be able to show the video. * A **remote video track** represents a remote peer video stream from a remote camera app. Specify `userId` for the remote camera app of the remote peer. * A **local video track** represents a local peer video stream from a local camera app. Specify `userId` for the local camera app of the local peer. `WebRTCView` subscribes to `QB.webrtc.EVENT_TYPE.RECEIVED_VIDEO_TRACK` event under the hood so you do not need to subscribe to it manually. Thus, once the SDK receives data that a remote video track was received, it creates the event of `RECEIVED_VIDEO_TRACK` type with `userId` and `sessionId` properties. If `userId` matches with the one in properties, the video starts playing. ```JavaScript JavaScript theme={null} import WebRTCView from 'quickblox-react-native-sdk/RTCView' //... render() { return ( //... <WebRTCView sessionId={session.id} style={styles.video} // add styles as necessary userId={userId} // your user's Id for local video or occupantId for remote /> ) } ``` | Parameters | Description | | ---------- | ------------------------ | | sessionId | Call session identifier. | | style | View styles. | | userId | User identifier. | Once `WebRTCView` is mounted, it is ready to play the video. Thus: * You can subscribe to event `QB.webrtc.EVENT_TYPE.RECEIVED_VIDEO_TRACK` and then add WebRTCViews. or * You can add WebRTCViews immediately and wait for the event of type `RECEIVED_VIDEO_TRACK` to come in and trigger playback. ## Event handler To process events such as incoming call, call reject, hang up, etc. you need to set up the event handler. The event handler processes various events that happen with the **call session** or **peer connection** in your app. The events are emitted by the WebRTC module of QuickBlox React Native SDK. <Note> Once the WebRTC module is initialized, it can start emitting events, so you can assign event handler even before module initialization. </Note> Using the callbacks provided by the event handler, you can implement and execute the event-related processing code. For example, the `accept()` method is called when you accept an incoming call from the opponent. This method receives information about the call session and additional key-value data about the user. QuickBlox React Native SDK persistently interacts with the server via **XMPP** connection that works as a **signaling** transport for establishing a call between two or more peers. It receives the callbacks of the asynchronous events which happen with the call and peer connection. This allows you to track these events and build your own video calling features around them. ### Call session events The table below lists all supported call session event types. | Event type | Description | | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | QB.webrtc.EVENT\_TYPE.CALL | An incoming call event has been received by the peer after the call session has been initiated. | | QB.webrtc.EVENT\_TYPE.AССEPT | An incoming call has been accepted by the peer. | | QB.webrtc.EVENT\_TYPE.REJECT | An incoming call has been rejected by the remote peer without accepting the call. | | QB.webrtc.EVENT\_TYPE.HANG\_UP | An accepted call has been ended by the peer by pressing the hang-up button. | | QB.webrtc.EVENT\_TYPE.RECEIVED\_VIDEO\_TRACK | A remote video track has been received by the remote peer. | | QB.webrtc.EVENT\_TYPE.PEER\_CONNECTION\_STATE\_CHANGED | A peer connection state has been changed. View all available peer connection states in the [Peer connection states section](#peer-connection-state). | | QB.webrtc.EVENT\_TYPE.NOT\_ANSWER | No answer received from the remote peer within the timer expiration period. | | QB.webrtc.EVENT\_TYPE.CALL\_END | An accepted call has been ended. A call session was closed. | To track call session events, you should add the event handler. ```JavaScript JavaScript theme={null} import { NativeEventEmitter } from 'react-native' import QB from 'quickblox-react-native-sdk' const emitter = new NativeEventEmitter(QB.webrtc) function onCall (event) { const { type, // "@QB/CALL" payload } = event const { userId, // id of QuickBlox user session, // incoming (new) session userInfo // custom data (object) } = payload // handle as necessary } function onCallAccept (event) { const { type, // "@QB/ACCEPT" payload } = event const { userId, // id of QuickBlox user session, // session userInfo // custom data (object) } = payload // handle as necessary } function onHangUp (event) { const { type, // "@QB/HANG_UP" payload } = event const { userId, // id of QuickBlox user session, // session userInfo // custom data (object) } = payload // handle as necessary } function onNotAnswer (event) { const { type, // "@QB/NOT_ANSWER" payload } = event const { userId, // id of QuickBlox user session // session } = payload // handle as necessary } function onReject (event) { const { type, // "@QB/REJECT" payload } = event const { userId, // id of QuickBlox user session, // session userInfo // custom data (object) } = payload // handle as necessary } function onCallEnd (event) { const { type, // "@QB/CALL_END" payload } = event const { session // session } = payload // handle as necessary } function onVideoTrackReceived (event) { const { type, // "@QB/RECEIVED_VIDEO_TRACK" payload } = event const { userId, // id of QuickBlox user sessionId // id of QuickBlox WebRTC session } = payload // handle as necessary } function onPeerStateChanged (event) { const { type, // "@QB/PEER_CONNECTION_STATE_CHANGED" payload } = event const { userId, // id of QuickBlox user session, // session state // new peerconnection state (one of QB.webrtc.RTC_PEER_CONNECTION_STATE) } = payload // handle as necessary } emitter.addListener(QB.webrtc.EVENT_TYPE.CALL, onCall) emitter.addListener(QB.webrtc.EVENT_TYPE.ACCEPT, onCallAccept) emitter.addListener(QB.webrtc.EVENT_TYPE.HANG_UP, onHangUp) emitter.addListener(QB.webrtc.EVENT_TYPE.NOT_ANSWER, onNotAnswer) emitter.addListener(QB.webrtc.EVENT_TYPE.REJECT, onReject) emitter.addListener(QB.webrtc.EVENT_TYPE.CALL_END, onCallEnd) emitter.addListener(QB.webrtc.EVENT_TYPE.RECEIVED_VIDEO_TRACK, onVideoTrackReceived) emitter.addListener(QB.webrtc.EVENT_TYPE.PEER_CONNECTION_STATE_CHANGED, onPeerStateChanged) ``` Each event emitted by WebRTC is an object type having the following fields: | Field | Description | | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | type | The name of the event you have subscribed to (for example, QB.webrtc.EVENT\_TYPE.CALL, QB.webrtc.EVENT\_TYPE.REJECT, etc). | | payload | Available if the event transmits the data. Almost all events contain the **session** (to identify in which session this event occurred) and **userId** (to indicate initiator of the event) properties. | Go to the [Resources](#resources) section to see a sequence diagram for a regular call workflow. ### Peer connection state The peer connection state can change. To monitor the states of your peer connections (users), you need to add the event handler for `QB.webrtc.EVENT\_TYPE.PEER\_CONNECTION\_STATE\_CHANGED` event type. ```JavaScript JavaScript theme={null} function onPeerStateChanged (event) { const { type, // "@QB/PEER_CONNECTION_STATE_CHANGED" payload } = event const { userId, // id of QuickBlox user session, // session state // new peerconnection state (one of QB.webrtc.RTC_PEER_CONNECTION_STATE) } = payload // handle as necessary } emitter.addListener( QB.webrtc.EVENT_TYPE.PEER_CONNECTION_STATE_CHANGED, onPeerStateChanged ); ``` The following table lists all supported peer connection states. | Connection state | Description | | --------------------------------------------------- | ---------------------------------------------- | | QB.webrtc.RTC\_PEER\_CONNECTION\_STATE.NEW | Gathering information to establish connection. | | QB.webrtc.RTC\_PEER\_CONNECTION\_STATE.CONNECTED | A peer is connected to a call session. | | QB.webrtc.RTC\_PEER\_CONNECTION\_STATE.FAILED | A peer failed to join a call session. | | QB.webrtc.RTC\_PEER\_CONNECTION\_STATE.DISCONNECTED | A peer is disconnected from a call session. | | QB.webrtc.RTC\_PEER\_CONNECTION\_STATE.CLOSED | A call session is closed by the peer. | ## Resources A regular call workflow. <Frame> <img alt="React_Native_SDK_initiate_accept_hang_up.png" /> </Frame> # Advanced Source: https://docs.quickblox.com/sdks/react-native-video-calling-advanced Learn how to mute audio, disable video, switch camera and audio output, etc. ## Mute audio Mute the audio by calling `enableAudio()` method with `sessionId`, `userId`, and `enable` parameters. Using this method we can tell SDK to send/not send audio data either from a local or remote peer in the specified call session. ```JavaScript JavaScript theme={null} const enableAudioParams = { sessionId, userId, enable: false }; QB.webrtc .enableAudio(enableAudioParams) .then(() => { /* muted audio successfully */ }) .catch(e => { /* handle error */ }) ``` The `enableAudio()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sessionId | yes | Call session identifier. | | userId | yes | ID of the user. This is an optional parameter. If userId is not passed to this method, a local audio stream is muted/unmuted. If userId is passed, a remote audio stream is muted/unmuted (by userid provided). | | enable | yes | Boolean parameter. true is enabled, false is disabled. | ## Disable video Turn off the video by calling the `enableVideo()` method with `sessionId`, `userId`, and `enable` parameters. Using this method we can tell SDK not to send video data either from a local or remote peer in the specified call session. The video is disabled when the argument is set to `false` and enabled when `true`. ```JavaScript JavaScript theme={null} const enableVideoParams = { sessionId, userId, enable: false }; QB.webrtc .enableVideo(enableVideoParams) .then(() => { /* muted video successfully */ }) .catch(e => { /* handle error */ }) ``` The `enableVideo()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sessionId | yes | Call session identifier. | | userId | no | ID of the user. If userId is not passed to this method, a local video stream is turned on/off. If userId is passed, a remote video stream is rendered/not rendered (by userId provided). | | enable | yes | Boolean parameter. true is enabled, false is disabled. | ## Switch camera You can switch the video camera during a call (**by default** is front camera) by passing `switchCamera()` with `sessionId` parameter. ```JavaScript JavaScript theme={null} const switchCameraParams = { sessionId }; QB.webrtc .switchCamera(switchCameraParams) .then(() => { /* camera switched successfully */ }) .catch(e => { /* handle error */ }) ``` The `switchCamera()` method accepts one argument of the object type that has the following fields: | Field | Required | Description | | --------- | -------- | ------------------------ | | sessionId | yes | Call session identifier. | ## Mirror local video Mirror functionality allows to flip the video horizontally. Enable the mirror by setting `mirror` prop as `true`. ```JavaScript JavaScript theme={null} import WebRTCView from 'quickblox-react-native-sdk/RTCView' //... render() { //... <WebRTCView mirror={true} sessionId={sessionId} style={styles.video} userId={userId} /> //... } ``` | Parameters | Description | | ---------- | ------------------------------------------------------ | | mirror | Boolean parameter. true is enabled, false is disabled. | | sessionId | Call session identifier. | | style | View style. | | userId | ID of the user. | ## Switch audio output You can switch the audio output to a loudspeaker or to ear-speaker during a call. ```JavaScript JavaScript theme={null} const audioOutputParams = { output: QB.webrtc.AUDIO_OUTPUT.LOUDSPEAKER }; QB.webrtc .switchAudioOutput(audioOutputParams) .then(() => { /* audio should now go through loudspeaker */ }) .catch(e => { /* handle error */ }) ``` ## Custom ICE servers <Warning> This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://assist.quickblox.com/). </Warning> ## General settings You can change different settings for your calls using `QB.rtcconfig` class. All of them are listed below. ### Answer time interval If an opponent hasn't answered you within an answer time interval, the `QB.webrtc.EVENT_TYPE.NOT_ANSWER` event type will be received. The answer time interval shows how much time an opponent has to answer your call. Set the answer time interval using the code snippet below. ```JavaScript JavaScript theme={null} QB.rtcconfig .setAnswerTimeInterval(30) // value should be a number and should be >= 10 .then(function () { /* success */ }) .catch(function (error)) { /* handle error */ } ``` Get the answer time interval using the code snippet below. ```JavaScript JavaScript theme={null} QB.rtcconfig .getAnswerTimeInterval() .then(function (interval) { }) .catch(function (error) { /* handle error */ }) ``` <Note> **By default**, the answer time interval is 60 seconds. The **minimum** value is 10 seconds. </Note> ### Dialing time interval Dialing time interval indicates how often to notify your opponents about your call. Set the dialing time interval using the code snippet below. ```JavaScript JavaScript theme={null} QB.rtcconfig .setDialingTimeInterval(5) // value should be a number and should be >= 3 .then(function () { /* success */ }) .catch(function (error) { /* handle error */ }) ``` Get the dialing time interval using the code snippet below. ```JavaScript JavaScript theme={null} QB.rtcconfig .getDialingTimeInterval() .then(function (interval) { }) .catch(function (error) { /* handle error */ }) ``` <Note> **By default**, the dialing time interval is 5 seconds. The **minimum** value is 3 seconds. </Note> # Video Conference Source: https://docs.quickblox.com/sdks/react-native-video-conference Learn how to add video conference calls to your app. This feature is coming soon. Please check back later. For more updates and questions, feel free to contact our [Customer Support Team](https://help.quickblox.com/). # Overview Source: https://docs.quickblox.com/sdks/sdk-overview Learn how to add QuickBlox to your app and send your first message. <CardGroup> <Card title="iOS" icon="apple"> Learn how to add QuickBlox to your iOS app and send your first message.<br /> <a href="/sdks/ios-quick-start"> <Icon icon="rocket" /> QuickStart </a> <br /> <a href="https://github.com/QuickBlox/quickblox-ios-sdk/archive/master.zip"> <Icon icon="download" /> Download samples </a> <br /> <a href="https://github.com/QuickBlox/quickblox-ios-sdk"> <Icon icon="github" /> View on GitHub </a> </Card> <Card title="Android" icon="android"> Learn how to add QuickBlox to your Android app and send your first message.<br /> <a href="/sdks/android-quick-start"> <Icon icon="rocket" /> QuickStart </a> <br /> <a href="https://github.com/QuickBlox/quickblox-android-sdk/archive/master.zip"> <Icon icon="download" /> Download samples </a> <br /> <a href="https://github.com/QuickBlox/quickblox-android-sdk"> <Icon icon="github" /> View on GitHub </a> </Card> <Card title="JavaScript" icon="js"> Learn how to add QuickBlox to your web app and send your first message.<br /> <a href="/sdks/js-quick-start"> <Icon icon="rocket" /> QuickStart </a> <br /> <a href="https://github.com/QuickBlox/quickblox-javascript-sdk/archive/gh-pages.zip"> <Icon icon="download" /> Download samples </a> <br /> <a href="https://github.com/QuickBlox/quickblox-javascript-sdk"> <Icon icon="github" /> View on GitHub </a> </Card> <Card title="React Native" icon="react"> Learn how to add QuickBlox to your React Native app and send your first message.<br /> <a href="/sdks/react-native-quick-start"> <Icon icon="rocket" /> QuickStart </a> <br /> <a href="https://github.com/QuickBlox/quickblox-react-native-samples/archive/master.zip"> <Icon icon="download" /> Download samples </a> <br /> <a href="https://github.com/QuickBlox/quickblox-react-native-samples/"> <Icon icon="github" /> View on GitHub </a> </Card> <Card title="Flutter" icon="flutter"> Learn how to add QuickBlox to your Flutter app and send your first message.<br /> <a href="/sdks/flutter-quick-start"> <Icon icon="rocket" /> QuickStart </a> </Card> <Card title="Server API" icon="server"> Learn how to add QuickBlox to your server app.<br /> <a href="https://docs.quickblox.com/reference/overview"> <Icon icon="rocket" /> QuickStart </a> </Card> </CardGroup> # AI Features Source: https://docs.quickblox.com/ui-kits/android-uikit-ai-features QuickBlox AI for Android provides a range of features to enhance the chat experience. ### Overview With essential messaging functionalities such as answer assistant, users can engage in more interactive conversations, and **AI Translate** can improve cross-language communication. **Supported features** | name | description | | ------------------- | -------------------------------------- | | AI Answer assistant | Provides answers based on chat history | | AI Translate | Provides translation of chat message | | AI Rephrase | Provides multiple variants of text | ## AI Answer assistant **QuickBlox Android UIKit** provides answer assistant functionality that helps users effortlessly send various answers considering chat history. ### How to use In Android UIKit version 0.10.0 and above, with Android QuickBlox SDK v4.2.1, you should enable QuickBlox AI Answer Assistant using your Smart ChatAssist ID from the QuickBlox admin panel. #### Enable AI Extensions 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Extensions** page 2. Select the checkboxes for the features you want to enable. 3. Click the Save button to save changes. <Frame> <img alt="Enable AI Extensions" /> </Frame> #### Review your AI Agent To get **ID** of AI Agent to use it as `smartChatAssistantId` param follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Agent** page 2. Choose the ID of the AI Agent you want to update and click on it. 3. Edit the AI Agent settings. 4. Click the Save button to save changes. <Frame> <img alt="Dashboard AI Agent" /> </Frame> #### Enable with AI Agent ID ```Kotlin Kotlin theme={null} val smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" QuickBloxUiKit.enableAIAnswerAssistantWithSmartChatAssistantId(smartChatAssistantId) ``` | parameter name | type | description | | :------------------- | :----- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual AI Agent ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | #### Enable with Open AI Token or Proxy server <Tip> **Recommendation** The code snippets below are not recommended for use with Android UIKit version 0.10.0 and above, as they will be deprecated. In the latest version of our Android UIKit, AI Assist customizations are no longer supported. </Tip> The [QuickBlox QBAIAnswerAssistant library](https://github.com/QuickBlox/android-ai-releases/tree/main/com/quickblox/android-ai-answer-assistant) is based on OpenAI functional and provides the simplest way to communicate with API. <iframe title="YouTube video player" /> All AI features in **QuickBlox UIKit for Android** are enabled by default, but for this functionality to work, you need to set either an **Open AI token** or a **Proxy server**. There are 2 configurations to use the **AIAnswerAssistant** functionality in **QuickBlox Android UIKit**. 1. **Directly**. Using the **AIAnswerAssistant** with raw **Open AI token**. The **QuickBlox Android UIKit** will communicate directly with OpenAI API. This is not the preferred way because we using **Open AI token** directly. It's a good solution for development flow, not for release 2. **Proxy**. Using the **AIAnswerAssistant** with QuickBlox session token and with a proxy server. The **QuickBlox Android UIKit** will communicate through a proxy server (not directly) with OpenAI API. This is the preferred safety way because we encapsulate the **Open AI token** inside the proxy server. It's a good solution for release flow. **Directly using** ```Kotlin Kotlin theme={null} val openAIToken = "open_ai_token" // our raw open ai token QuickBloxUiKit.enableAIAnswerAssistantWithOpenAIToken(openAIToken) ``` | parameter name | type | description | | -------------- | ------ | ---------------------------------- | | openAIToken | String | The token for access to OpenAI API | **Proxy using** ```Kotlin Kotlin theme={null} val serverProxyUrl = "https://my.proxy-server.com" // our proxy server url QuickBloxUiKit.enableAIAnswerAssistantWithProxyServer(serverProxyUrl) ``` | parameter name | type | description | | -------------- | ------ | ------------------------- | | serverProxyUrl | String | The URL of a proxy server | <Tip> Recommendation We recommended using our **proxy server** which you can find by link [https://github.com/QuickBlox/qb-ai-assistant-proxy-server/releases](https://github.com/QuickBlox/qb-ai-assistant-proxy-server/releases) Please use the latest release. </Tip> #### Disable AIAnswerAssistant To disable the **AIAnswerAssistant** functionality in **QuickBlox Android UIKit** you can call the method: ```Kotlin Kotlin theme={null} QuickBloxUiKit.disableAIAnswerAssistant() ``` #### Customization **QuickBlox Android UIKit** has the ability to set additional query parameters for **AIAnswerAssistant**, for example: ```Kotlin Kotlin theme={null} val openAIModel: Model = Model.GPT_3_5_TURBO val maxRequestTokens: Int = 3000 val maxResponseTokens: Int = 1000 val organization: String = "Organization" val temperature: Float = 0.5f QuickBloxUiKit.setOpenAIModelForAIAnswerAssistant(openAIModel) QuickBloxUiKit.setMaxRequestTokensForAIAnswerAssistant(maxRequestTokens) QuickBloxUiKit.setMaxResponseTokensForAIAnswerAssistant(maxResponseTokens) QuickBloxUiKit.setOrganizationForAIAnswerAssistant(organization) QuickBloxUiKit.setTemperatureForAIAnswerAssistant(temperature) ``` | parameter name | type | description | | ----------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- | | openAIModel | Model | Model of Open AI | | maxRequestTokens | Int | Maximum number of tokens for a request | | maxResponseTokens | Int | Maximum number of tokens for a response | | organization | String | The organization ID is the unique identifier for your organization which can be used in API requests. | | temperature | Float | The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 | ## AI Translate QuickBlox provides Translate functionality that helps users simplify cross-lingual communication by providing instant message translation services. <img /> ### How to use In Android UIKit version 0.10.0 and above, with Android QuickBlox SDK v4.2.1, you should enable QuickBlox AI Translate using your Smart ChatAssist ID from the QuickBlox admin panel. #### Enable AI Extensions 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Extensions** page 2. Select the checkboxes for the features you want to enable. 3. Click the Save button to save changes. <Frame> <img alt="Enable AI Extensions" /> </Frame> #### Review your AI Agent To get **ID** of AI Agent to use it as `smartChatAssistantId` param follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Agent** page 2. Choose the ID of the AI Agent you want to update and click on it. 3. Edit the AI Agent settings. 4. Click the Save button to save changes. <Frame> <img alt="Dashboard AI Agent" /> </Frame> #### Enable with AI Agent ID ```Kotlin Kotlin theme={null} val smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" QuickBloxUiKit.enableAITranslateWithSmartChatAssistantId(smartChatAssistantId) ``` | parameter name | type | description | | :------------------- | :----- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual AI Agent ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | #### Enable with Open AI Token or Proxy server <Tip> **Recommendation** The code snippets below are not recommended for use with Android UIKit version 0.10.0 and above, as they will be deprecated. In the latest version of our Android UIKit, AI Assist customizations are no longer supported. </Tip> The [QuickBlox AITranslate library](https://github.com/QuickBlox/android-ai-releases/tree/main/com/quickblox/android-ai-translate) is based on OpenAI functional and provides the simplest way to communicate with API. All AI features in QuickBlox UIKit for Android are enabled by default, but for this functionality to work, you need to set either an **Open AI token** or a **Proxy server**. There are 2 configurations to use the **AITranslate** functionality in **QuickBlox Android UIKit**. 1. **Directly**. Using the **AITranslate** with raw **Open AI token**. The **QuickBlox UIKit** will communicate directly with OpenAI API. This is not the preferred way because we using **Open AI token** directly. It's a good solution for development flow, not for release 2. **Proxy**. Using the **AITranslate** with QuickBlox session token and with a proxy server. The **QuickBlox UIKit** will communicate through a proxy server (not directly) with OpenAI API. This is the preferred safety way because we encapsulate the **Open AI token** inside the proxy server. It's a good solution for release flow. **Directly using** ```Kotlin Kotlin theme={null} val openAIToken = "open_ai_token" // our raw open ai token QuickBloxUiKit.enableAITranslateWithOpenAIToken(openAIToken) ``` | parameter name | type | description | | -------------- | ------ | ---------------------------------- | | openAIToken | String | The token for access to OpenAI API | **Proxy using** ```Kotlin theme={null} val serverProxyUrl = "https://my.proxy-server.com" // our proxy server url QuickBloxUiKit.enableAITranslateWithProxyServer(serverProxyUrl) ``` | parameter name | type | description | | -------------- | ------ | ------------------------- | | serverProxyUrl | String | The URL of a proxy server | <Tip> Recommendation We recommended using our **proxy server** which you can find by link [https://github.com/QuickBlox/qb-ai-assistant-proxy-server/releases](https://github.com/QuickBlox/qb-ai-assistant-proxy-server/releases) Please use the latest release. </Tip> #### Disable AITranslate To disable the **AITranslate** functionality in **QuickBlox Android UIKit** you can call the method: ```Kotlin Kotlin theme={null} QuickBloxUiKit.disableAITranslate() ``` #### Customization **QuickBlox Android UIKit** uses the language set in the device system to translate messages. You can also set the custom language for translation using the method: ```Kotlin Kotlin theme={null} QuickBloxUiKit.setupLanguageToAITranslate(Languages.ITALIAN) ``` If **QuickBlox Android UIKit** does not support the language of your system or the one you want to set, then the default language will be `Languages.ENGLISH`. **QuickBlox Android UIKit** also has the ability to set additional query parameters for **AITranslate**, for example: ```Kotlin Kotlin theme={null} val openAIModel: Model = Model.GPT_3_5_TURBO val maxRequestTokens: Int = 3000 val maxResponseTokens: Int = 1000 val organization: String = "Organization" val temperature: Float = 0.5f QuickBloxUiKit.setOpenAIModelForAITranslate(openAIModel) QuickBloxUiKit.setMaxRequestTokensForAITranslate(maxRequestTokens) QuickBloxUiKit.setMaxResponseTokensForAITranslate(maxResponseTokens) QuickBloxUiKit.setOrganizationForAITranslate(organization) QuickBloxUiKit.setTemperatureForAITranslate(temperature) ``` | parameter name | type | description | | ----------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- | | openAIModel | Model | Model of Open AI | | maxRequestTokens | Int | Maximum number of tokens for a request | | maxResponseTokens | Int | Maximum number of tokens for a response | | organization | String | The organization ID is the unique identifier for your organization which can be used in API requests. | | temperature | Float | The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 | ## AI Rephrase **QuickBlox UIKit for Android** provides AI Rephrase functionality that helps users effortlessly modify text with multiple variants using the [QuickBlox AIRephrase library](https://github.com/QuickBlox/android-ai-releases/tree/main/com/quickblox/android-ai-editing-assistant). <img /> ### How to use All AI features in **QuickBlox UIKit for Android** are enabled by default, but for this functionality to work, you need to set either an **Open AI token** or a **Proxy server**. #### Enable with Open AI Token or Proxy server There are 2 configurations to use the **AIRephrase** functionality in **QuickBlox Android UIKit**. 1. **Directly**. Using the **AIRephrase** with raw **Open AI token**. The **QuickBlox Android UIKit** will communicate directly with OpenAI API. This is not the preferred way because we using **Open AI token** directly. It's a good solution for development flow, not for release 2. **Proxy**. Using the **AIRephrase** with QuickBlox session token and with a proxy server. The **QuickBlox Android UIKit** will communicate through a proxy server (not directly) with OpenAI API. This is the preferred safety way because we encapsulate the **Open AI token** inside the proxy server. It's a good solution for release flow. **Directly using** ```Kotlin Kotlin theme={null} val openAIToken = "open_ai_token" // our raw open ai token QuickBloxUiKit.enableAIRephraseWithOpenAIToken(openAIToken) ``` | parameter name | type | description | | -------------- | ------ | ---------------------------------- | | openAIToken | String | The token for access to OpenAI API | **Proxy using** ```Kotlin Kotlin theme={null} val serverProxyUrl = "https://my.proxy-server.com" // our proxy server url QuickBloxUiKit.enableAIRephraseWithProxyServer(serverProxyUrl) ``` | parameter name | type | description | | -------------- | ------ | ------------------------- | | serverProxyUrl | String | The URL of a proxy server | <Tip> Recommendation We recommended using our **proxy server** which you can find by link [https://github.com/QuickBlox/qb-ai-assistant-proxy-server/releases](https://github.com/QuickBlox/qb-ai-assistant-proxy-server/releases) Please use the latest release. </Tip> #### Disable AIRephrase To disable the **AIRephrase** functionality in **QuickBlox Android UIKit** you can call the method: ```Kotlin Kotlin theme={null} QuickBloxUiKit.disableAIRephrase() ``` #### Customization The developer has the ability to set his own list of tones, for example: ```Kotlin Kotlin theme={null} val tones = mutableListOf<AIRephraseToneEntity>() val professionalTone = AIRephraseToneEntityImpl("Professional", "This would edit messages to sound more formal, using technical vocabulary, clear sentence structures, and maintaining a respectful tone. It would avoid colloquial language and ensure appropriate salutations and sign-offs.", "\uD83D\uDC54") val friendlyTone = AIRephraseToneEntityImpl("Friendly", "This would adjust messages to reflect a casual, friendly tone. It would incorporate casual language, use emoticons, exclamation points, and other informalities to make the message seem more friendly and approachable.", "\uD83E\uDD1D") tones.add(professionalTone) tones.add(friendlyTone) QuickBloxUiKit.setRephraseTones(tones) ``` | parameter name | type | description | | -------------- | ---- | -------------------- | | tones | List | List of custom tones | **QuickBlox Android UIKit** also has the ability to set additional query parameters for **AIRephrase**, for example: ```Kotlin Kotlin theme={null} val openAIModel: Model = Model.GPT_3_5_TURBO val maxRequestTokens: Int = 3000 val maxResponseTokens: Int = 1000 val organization: String = "Organization" val temperature: Float = 0.5f QuickBloxUiKit.setOpenAIModelForAIRephrase(openAIModel) QuickBloxUiKit.setMaxRequestTokensForAIRephrase(maxRequestTokens) QuickBloxUiKit.setMaxResponseTokensForAIRephrase(maxResponseTokens) QuickBloxUiKit.setOrganizationForAIRephrase(organization) QuickBloxUiKit.setTemperatureForAIRephrase(temperature) ``` | parameter name | type | description | | ----------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- | | openAIModel | Model | Model of Open AI | | maxRequestTokens | Int | Maximum number of tokens for a request | | maxResponseTokens | Int | Maximum number of tokens for a response | | organization | String | The organization ID is the unique identifier for your organization which can be used in API requests. | | temperature | Float | The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 | **Default tones** * **Professional tone**. This will allow you to edit messages to sound more formal, using technical language, clear sentence structures, and maintaining a respectful tone. This would avoid colloquial language and ensure appropriate greetings and signatures. * **Friendly tone**. This will allow you to tailor your messages to reflect a casual, friendly tone. It will include casual language, use emoticons, exclamation points, and other informal elements to make the message seem more friendly and approachable. * **Encouraging tone**. This tone will be useful for motivation and encouragement. It will include positive words, and affirmations and express support and faith in the recipient * **Empathic tone**. This tone will be used to show understanding and empathy. This will require softer language, acknowledgment of feelings, and demonstrations of compassion and support. * **Neutral tone**. For when you want to maintain an even, impartial, and objective tone. He will avoid harsh statements and emotional words, preferring clear and direct communication * **Assertive tone**. This tone is useful for making a clear statement, asserting a position, or in negotiations. He uses direct speech, is confident, and does not soften his words. * **Instructive tone**. This tone would be useful for tutorials, guides, or other teaching and learning materials. It is clear, concise and logically guides the reader through the steps or processes. * **Persuasive tone**. This tone can be used when trying to convince someone or to argue your point. He uses persuasive language, strong words, and logical reasoning. * **Sarcastic/Ironic tone**. This tone can make the communication more humorous or convey an ironic attitude. It is more difficult to implement because it requires the AI to understand the nuances of the language and may not always be perceived by the reader as intended. * **Poetic tone**. This will add an artistic touch to messages by using figurative language, rhyme, and rhythm to create more expressive text. # Customization Source: https://docs.quickblox.com/ui-kits/android-uikit-customization The QuickBlox UIKit for Android allows you to create your own unique view of the UIKit. ## Themes ### Default themes **QuickBlox Android UIKit** comes with two preset themes: **light (LightUIKitTheme)** and **dark (DarkUIKitTheme)**. Both themes implement the **UIKitTheme** interface, which allows you to use them to customize the appearance of interface elements. #### Light Theme <Frame> <img alt="Light Theme" /> </Frame> #### Dark Theme <Frame> <img alt="Dark Theme" /> </Frame> ### Theme colors <Frame> <img alt="Theme Colors" /> </Frame> The default themes in **QuickBlox Android UIKit** already define primary colors corresponding to light and dark themes. However, you can also change the specific colors in the preset themes to tailor them to your needs. ```Kotlin Kotlin theme={null} val darkUiKitTheme: UiKitTheme = DarkUiKitTheme() val whiteColor = "#FFFFFF" darkUiKitTheme.setMainTextColor(whiteColor) QuickBloxUiKit.setTheme(darkUiKitTheme) ``` In the example, we set the white color for the main text in the **DarkUIKitTheme** and set this theme to **UIKit**. You can use the default pre-installed themes in **QuickBlox Android UIKit** and change only the colors you need by overriding the appropriate attributes. <Warning> Important Modified themes need to be set before launching UIKit screens and components. </Warning> ### Custom theme If you want to create a completely custom theme, it must also implement the **UIKitTheme** interface. You will be able to define and customize all colors and attributes to suit your needs. ```Kotlin Kotlin theme={null} class CustomUIKitTheme : UiKitTheme { private var mainBackgroundColor: String = "#FFFFFF" private var statusBarColor: String = "#E4E6E8" private var mainElementsColor: String = "#3978FC" private var secondaryBackgroundColor: String = "#E7EFFF" private var mainTextColor: String = "#0B121B" private var disabledElementsColor: String = "#BCC1C5" private var secondaryTextColor: String = "#636D78" private var secondaryElementsColor: String = "#202F3E" private var dividerColor: String = "#E7EFFF" private var incomingMessageColor: String = "#E4E6E8" private var outgoingMessageColor: String = "#E7EFFF" private var inputBackgroundColor: String = "#E4E6E8" private var tertiaryElementsColor: String = "#636D78" private var errorColor: String = "#FF766E" override fun getMainBackgroundColor(): Int { return parseColorToIntFrom(mainBackgroundColor) } override fun setMainBackgroundColor(colorString: String) { mainBackgroundColor = colorString } override fun getStatusBarColor(): Int { return parseColorToIntFrom(statusBarColor) } override fun setStatusBarColor(colorString: String) { statusBarColor = colorString } override fun getMainElementsColor(): Int { return parseColorToIntFrom(mainElementsColor) } override fun setMainElementsColor(colorString: String) { mainElementsColor = colorString } override fun getSecondaryBackgroundColor(): Int { return parseColorToIntFrom(secondaryBackgroundColor) } override fun setSecondaryBackgroundColor(colorString: String) { secondaryBackgroundColor = colorString } override fun setDisabledElementsColor(colorString: String) { disabledElementsColor = colorString } override fun getDisabledElementsColor(): Int { return parseColorToIntFrom(disabledElementsColor) } override fun getMainTextColor(): Int { return parseColorToIntFrom(mainTextColor) } override fun setMainTextColor(colorString: String) { mainTextColor = colorString } override fun setSecondaryTextColor(colorString: String) { secondaryTextColor = colorString } override fun getSecondaryTextColor(): Int { return parseColorToIntFrom(secondaryTextColor) } override fun setSecondaryElementsColor(colorString: String) { secondaryElementsColor = colorString } override fun getIncomingMessageColor(): Int { return parseColorToIntFrom(incomingMessageColor) } override fun setIncomingMessageColor(colorString: String) { incomingMessageColor = colorString } override fun getOutgoingMessageColor(): Int { return parseColorToIntFrom(outgoingMessageColor) } override fun setOutgoingMessageColor(colorString: String) { outgoingMessageColor = colorString } override fun getDividerColor(): Int { return parseColorToIntFrom(dividerColor) } override fun setDividerColor(colorString: String) { dividerColor = colorString } override fun getInputBackgroundColor(): Int { return parseColorToIntFrom(inputBackgroundColor) } override fun setInputBackgroundColor(colorString: String) { inputBackgroundColor = colorString } override fun getTertiaryElementsColor(): Int { return parseColorToIntFrom(tertiaryElementsColor) } override fun setTertiaryElementsColor(colorString: String) { tertiaryElementsColor = colorString } override fun getSecondaryElementsColor(): Int { return parseColorToIntFrom(secondaryElementsColor) } override fun getErrorColor(): Int { return parseColorToIntFrom(errorColor) } override fun setErrorColor(colorString: String) { errorColor = colorString } override fun parseColorToIntFrom(colorString: String): Int { try { return Color.parseColor(colorString) } catch (exception: IllegalArgumentException) { throw ParseColorException(exception.message.toString()) } catch (exception: NumberFormatException) { throw ParseColorException(exception.message.toString()) } } } ``` After which you need to add your custom theme to UIKit: ```Kotlin Kotlin theme={null} val customTheme: UiKitTheme = CustomUIKitTheme() QuickBloxUiKit.setTheme(customTheme) ``` <Warning> Important Modified themes need to be set before launching UIKit screens and components. </Warning> ## Screens ### Creating a custom screen factory The **QuickBlox Android UIKit** provides an interface called **ScreenFactory**, which allows you to customize the creation of screens in your Android application. The default implementation of this interface is **DefaultScreenFactory()**, which creates screens using Fragments. However, if you need to replace a specific screen with your screen, you can create your own implementation of the **ScreenFactory** interface. This custom implementation will be responsible for creating the desired screens according to your requirements. This allows you to tailor the screen creation process to suit your application's specific needs. ```Kotlin Kotlin theme={null} open class CustomScreenFactory : ScreenFactory { override fun createDialogs(screenSettings: DialogsScreenSettings?): Fragment { return DialogsFragment.newInstance(screenSettings) } override fun createDialogName(dialogEntity: DialogEntity?, screenSettings: DialogNameScreenSettings?): Fragment { return DialogNameFragment.newInstance(dialogEntity, screenSettings) } override fun createUsers(dialogEntity: DialogEntity?, screenSettings: UsersScreenSettings?): Fragment { return UsersFragment.newInstance(dialogEntity, screenSettings) } override fun createPrivateChat(dialogId: String?, screenSettings: PrivateChatScreenSettings?): Fragment { return PrivateChatFragment.newInstance(dialogId, screenSettings) } override fun createGroupChat(dialogId: String?, screenSettings: GroupChatScreenSettings?): Fragment { return GroupChatFragment.newInstance(dialogId, screenSettings) } override fun createGroupChatInfo(dialogId: String?, screenSettings: GroupChatInfoScreenSettings?): Fragment { return GroupChatInfoFragment.newInstance(dialogId, screenSettings) } override fun createPrivateChatInfo(dialogId: String?, screenSettings: PrivateChatInfoScreenSettings?): Fragment { return PrivateChatInfoFragment.newInstance(dialogId, screenSettings) } override fun createMembers(dialogId: String?, screenSettings: MembersScreenSettings?): Fragment { return MembersFragment.newInstance(dialogId, screenSettings) } override fun createAddMembers(dialogId: String?, screenSettings: AddMembersScreenSettings?): Fragment { return AddMembersFragment.newInstance(dialogId, screenSettings) } } ``` After which you need to set a custom factory in UIKit: ```Kotlin Kotlin theme={null} val customScreenFactory: ScreenFactory = CustomScreenFactory() QuickBloxUiKit.setScreenFactory(customScreenFactory) ``` <Warning> Important Modified ScreenFactory need to be set before launching UIKit screens and components. </Warning> ### Customizing screens In **QuickBlox Android UIKit**, each screen accepts an instance of a specific implementation of the **ScreenSettings** interface as a constructor parameter. This ScreenSettings object is responsible for configuring the components and settings of the screen. It's important to note that the **ScreenSettings** parameter is optional, and if not provided, the default implementation will be used. The ScreenSettings interface allows you to define the properties and behavior of the screen components. It provides methods to show or hide specific components and allows you to set custom components for a particular screen. This gives you the flexibility to customize the appearance and functionality of each screen according to your specific requirements. To create a **ScreenSettings** object, **QuickBlox Android UIKit** utilizes the Builder pattern. This pattern allows you to conveniently set the desired properties and configurations for the screen. You can chain multiple methods calls to set various settings, such as showing or hiding specific components, setting custom components, or modifying any other relevant screen attributes. By using the Builder pattern, you can create a **ScreenSettings** object with the desired configurations before passing it to the screen's constructor. This approach simplifies the process of customizing and fine-tuning the screen's appearance and behavior. ```Kotlin Kotlin theme={null} val darkUiKitTheme: UiKitTheme = DarkUiKitTheme() val dialogsScreenSettings = DialogsScreenSettings.Builder(context) .showDialogs(true) .showHeader(true) .showSearch(false) .setTheme(darkUiKitTheme) .setDialogsComponent(DialogsComponentImpl(context)) .setHeaderComponent(HeaderWithIconComponentImpl(context)) .setSearchComponent(SearchComponentImpl(context)) .build() DialogsActivity.show(this, dialogsScreenSettings) ``` In this example, we showed how to add **ScreenSettings** with your own setting to the dialogs screen. Of course, we don’t need to invoke all methods like in the code snippet above. We can invoke only the exact methods that we need. For example, if we need just to hide the search component the code will be like the snippet below: ```Kotlin Kotlin theme={null} val dialogsScreenSettings = DialogsScreenSettings.Builder(context) .showSearch(false) .build() DialogsActivity.show(this, dialogsScreenSettings) ``` <Tip> Quick start using our [Android UIKit sample](https://github.com/QuickBlox/quickblox-android-sdk/tree/master/android-ui-kit-sample) This sample implements authorization functionality and provides an example of color theme customization. Sample code available by this [link](https://github.com/QuickBlox/quickblox-android-sdk/tree/master/android-ui-kit-sample). How to set up and run a sample, see the article in out blog [Getting Started with the QuickBlox Android UIKit](https://quickblox.com/blog/getting-started-with-quickblox-android-ui-kit/). </Tip> # Features Source: https://docs.quickblox.com/ui-kits/android-uikit-features QuickBlox UIKit for Android provides a range of features to enhance the chat experience. ## Overview With essential messaging functionalities such as typing indicators, unread messages, file sharing etc., users can engage in more interactive conversations. **Supported features by dialog types** | | Group dialog | Private dialog | | ---------------------- | ------------ | -------------- | | `File sharing` | ✓ | ✓ | | `Voice message` | ✓ | ✓ | | `Typing indicator` | ✓ | ✓ | | `Delivery receipt` | ✓ | ✓ | | `Read receipt` | ✓ | ✓ | | `Unread message count` | ✓ | ✓ | | `Highlight URLs` | ✓ | ✓ | <Note> Public dialogs are not currently supported but will be added in the upcoming releases. </Note> ### Dialog types QuickBlox UIKit provides support for three types of dialogs: public dialog, group dialog, and private dialog. **Public dialog** A public dialog is an open chat that is shown to everyone in the app and anyone can join it. Public dialogs can host more participants compared to group dialogs, but does not have a list of participants and their online statuses within dialog. **Group dialog** A group dialog is a chat between closed group of people. New participant can be added either by dialog owner or any other participant of the group dialog. **Private dialog** A private dialog is a direct chat between two people, it can't be extended to host more people. ## File sharing QuickBlox provides file sharing functionality that enables users to effortlessly send various types of files while engaged in a chat session, such as images, audio files, or videos. File sharing appears on `SendMessageComponent`. <Frame> <img alt="Android UIKit Share File" /> </Frame> ### Customize the UI for file sharing You can customize the UI for file sharing using string resource and icon resource. **String resource** The string resource is a set of strings used to compose the screen. It's a **res/strings.xml** file containing UIKit-defined string values. ```xml xml theme={null} <resources> <string name="send_attachment">Send attachment</string> <string name="take_photo">Take photo from camera</string> <string name="take_video">Take video from camera</string> <string name="photo_video">Photo and video</string> <string name="file">File</string> </resources> ``` **Icon resource** Icon resources are stored in **drawable** directory, all icons are vectors in XML file. The following table shows customizable file sharing icon. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>attachment</td> <td> <img alt="attachment" /> </td> <td>An icon used to select and add files.</td> </tr> <tr> <td>ic\_video\_placeholder</td> <td> <img alt="video placeholder" /> </td> <td>An icon for video placeholder.</td> </tr> <tr> <td>ic\_gif\_placeholder</td> <td> <img alt="gif placeholder" /> </td> <td>An icon for gif placeholder.</td> </tr> <tr> <td>ic\_image\_placeholder</td> <td> <img alt="image placeholder" /> </td> <td>An icon for image placeholder.</td> </tr> <tr> <td>ic\_file</td> <td> <img alt="file" /> </td> <td>An icon for file.</td> </tr> </tbody> </table> ## Voice message Voice messaging is another way for people to communicate without typing text. With voice messages, you can express yourself better by using tone and intention. It's also quicker to record a voice message than to write a long text, which makes it more efficient for the sender. The receiver can listen to the message whenever it's convenient for them, even while doing other tasks. To send a voice message in a dialog, you just need to press and hold the mic icon. Then, the message will appear in the chat, and the receiver can play it. <img alt="" /> ### Send a voice message In order to send a voice message, users have to record a message first. By default, a voice message icon is visible on the right side of the input field. While the icon is pressed, the voice recorder appears and recording the sound from microphone. The voice message icon appears only when input text form is empty. When the icon unpressed the voice message will send automatically. ### Customize the UI for voice message recorder You can customize the UI for voice message recorder using icon resource. **Icon resource** Icon resources are stored in **drawable** directory, all icons are vectors in XML file. The following table shows customizable file sharing icon. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>send\_voice</td> <td> <img alt="send voice" /> </td> <td>An icon for send voice.</td> </tr> <tr> <td>ic\_record</td> <td> <img alt="recorder" /> </td> <td>An icon for recorder.</td> </tr> </tbody> </table> ### Customize the UI for voice messages You can customize the UI for voice messages using icon resource. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>ic\_play</td> <td> <img alt="audio" /> </td> <td>An icon for audio.</td> </tr> <tr> <td>equalizer</td> <td> <img alt="audio background" /> </td> <td>An icon for audio message background.</td> </tr> </tbody> </table> ## Typing indicator The typing indicator is a useful feature that provides users with a visual signal when someone else in the dialog is typing a message. This feature is implemented in the `PrivateChatViewModel` of the `PrivateChatFragment` and `GroupChatViewModel` of the `GroupChatFragment`. The indicator remains visible until the user sends the message or clears the text. If the user stops typing for more than 10 seconds, the indicator will also disappear. <img alt="" /> ## Delivery receipt The delivery receipt is a feature that informs a user if their messages have been successfully delivered to other users in the dialog. When message reaches server, it is marked as sent, a single-tick icon appears next to the message’s timestamp. Once message reaches receiver device, it is marked as delivered. If the sender's message has been delivered to any recipient in the dialog, a double-tick icon appears next to the message's timestamp. The delivery receipt can only be seen by the sender of the message. It appears on the `MessagesComponent`. <img alt="" /> ### Customize the UI for delivery receipt You can customize the UI for delivery receipt using icon resource. **Icon resource** Icon resources are stored in **drawable** directory, all icons are vectors in XML file. The following table shows customizable file sharing icon. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>sent</td> <td> <img alt="sent" /> </td> <td>An icon for sent.</td> </tr> <tr> <td>delivered</td> <td> <img alt="delivery" /> </td> <td>An icon for delivery.</td> </tr> </tbody> </table> <Tip> The default delivery receipt color uses the **tertiary elements color**, it can be changed in the theme with `setTertiaryElementsColor()` (see [Theme colors](/ui-kits/android-uikit-customization#theme-colors)) </Tip> ## Read receipt The read receipt feature allows users to determine whether their messages have been read by others in the dialog. When any recipient in the dialog have read the sender's message, a colored double-tick icon is displayed next to the message's timestamp. The read receipts are only visible to the sender of the message. It appears on the `MessagesComponent`. <img alt="" /> ### Customize the UI for read receipt You can customize the UI for read receipt using icon resource. **Icon resource** Icon resources are stored in **drawable** directory, all icons are vectors in XML file. The following table shows customizable file sharing icon. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>read</td> <td> <img alt="read" /> </td> <td>An icon for read.</td> </tr> </tbody> </table> <Tip> The default read receipt color uses the **main elements color**, it can be changed in the theme with `setMainElementsColor()` (see [Theme colors](/ui-kits/android-uikit-customization#theme-colors)) </Tip> ## Unread message count Unread message count is a feature that informs users of the number of messages they haven’t read in each dialog. The number of unread messages per dialog is displayed below the timestamp. If the number of messages is more than 100, it will be displayed as `99+`. The unread message count appears on the `DialogsComponent`. <img alt="" /> ### Customize the UI for unread message counts You can customize the UI for unread message count using shape resource. **Shape resource** Shape resources are stored in **drawable** directory. <table> <thead> <tr> <th>Shape name</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>bg\_dialog\_counter</td> <td>Shape for unread message count</td> </tr> </tbody> </table> <Note> The default shape color uses the **main elements color**, it can be changed in the theme with `setMainElementsColor()` (see [Theme colors](/ui-kits/android-uikit-customization#theme-colors)) </Note> ## Highlight URLs The highlighting URLs is a feature that refers to displaying clickable links in a chat message, where the URL is visually distinguished and can be interacted with the user. <img alt="" /> ## Forwarding and Replying Messages Modern messengers and chat platforms offer various functional features, among the most useful of which are message forwarding and reply options. Let's take a closer look at them. <img /> By default, these functions are enabled, but if necessary, they can be disabled and enabled using special methods: ```Kotlin Kotlin theme={null} QuickBloxUiKit.enableReply() QuickBloxUiKit.disableReply() QuickBloxUiKit.enableForward() QuickBloxUiKit.disableForward() ``` ### Forwarding Messages: Forwarding is the capability to transfer messages from one chat participant to another, similar to email forwarding. It enables efficient exchange and discussion of information within the context of chats and messengers. <img /> Once you've selected the dialogue to which you want to forward the message, the forwarded message will be displayed in the selected dialogue like this. <img /> ### Replying to Messages: Replying is the ability to respond to a specific message, establishing a connection between the reply and the original message. It enhances the structure of communication in a chat, making conversations more clear and organized. <img /> After choosing the message to which you've decided to reply, your response to the message will be displayed like this. <img /> ### Customize the UI for forwarding and replying: You can customize the UI for file sharing using string resources and icon resources. **String resource** The string resource is a set of strings used to compose the screen. It's a **res/strings.xml** file containing UIKit-defined string values. ```xml xml theme={null} <resources> <string name="forward">Forward</string> <string name="forwarded_from_with_name">Forwarded from %s</string> <string name="replied_to_with_name">Replied to %s</string> <string name="replied_to">Replied to</string> <string name="forwarded_from">Forwarded from</string> <string name="message_forwarded_to">Message forwarded to %s</string> <string name="reply">Reply</string> </resources> ``` **Icon resource** Icon resources are stored in **drawable** directory, all icons are vectors in XML file. The following table shows customizable file sharing icon. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>ic\_forward</td> <td> <img /> </td> <td>An icon for forward.</td> </tr> <tr> <td>ic\_reply</td> <td> <img /> </td> <td>An icon for reply.</td> </tr> </tbody> </table> # Overview Source: https://docs.quickblox.com/ui-kits/android-uikit-overview The main goal of the QuickBlox UIKit for Android is to offer developers a streamlined and efficient way to implement chat functionality within their Android applications. The **QuickBlox UIKit for Android** is a comprehensive user interface kit specifically designed for building chat applications. It provides a collection of pre-built components, modules, and utilities that simplify the process of creating chat applications. The main goal of the **QuickBlox UIKit for Android** is to offer developers a streamlined and efficient way to implement chat functionality within their Android applications. The **QuickBlox UIKit for Android** offers modules that encapsulate complex chat functionalities, such as dialogs and chat management, and real-time updates. These modules provide a simplified interface for integrating chat features into applications without the need for extensive knowledge of the underlying protocols or server-side infrastructure. <Frame> <img alt="Android Doc" /> </Frame> <Frame> <img alt="Android Doc Dark" /> </Frame> ## Features **QuickBlox UIKit for Android** provides next functionality: * List of dialogs * Create dialog(Private or Group) * Dialog screen * Send text, image, video, audio, file messages * Dialog info screen * List, invite, remove members ## Source code You can find **QuickBlox UIKit for Android** source code [here](https://github.com/QuickBlox/android-ui-kit) ## Sample <Tip> Quick start using our [Android UIKit sample](https://github.com/QuickBlox/quickblox-android-sdk/tree/master/android-ui-kit-sample) This sample implements authorization functionality and provides an example of color theme customization. Sample code available by this [link](https://github.com/QuickBlox/quickblox-android-sdk/tree/master/android-ui-kit-sample). How to set up and run a sample, see the article in out blog [Getting Started with the QuickBlox Android UIKit](https://quickblox.com/blog/getting-started-with-quickblox-android-ui-kit/). </Tip> # Screens Source: https://docs.quickblox.com/ui-kits/android-uikit-screens QuickBlox Android UIKit allows you to create customizable screens and execute main chat functions such as list dialogs, create dialogs, and chat in dialogs. ## Overview Screens are made up of three main parts: `Fragment`, `ScreenSettings`, and `ViewModel`. Each fragment has a specific `ScreenSettings` that creates the view and each `ScreenSettings` contains customizable UI components. A fragment also has a corresponding `ViewModel` that provides the necessary data from **QuickBlox Android SDK**. Refer to the table below to see which screens we provide and the components that make up each screen. | Screen | Fragment | ScreenSettings | ViewModel | Components | | ---------------------------------------------------------------------------- | ----------------------- | ----------------------------- | ------------------------ | ------------------------------------------ | | [Dialog list](/ui-kits/android-uikit-screens#dialog-list-screen) | DialogsFragment | DialogsScreenSettings | DialogsViewModel | HeaderWithIconComponentDialogsComponent | | [Dialog name](/ui-kits/android-uikit-screens#dialog-name-screen) | DialogNameFragment | DialogNameScreenSettings | DialogNameViewModel | HeaderWithTextComponentDialogNameComponent | | [User list](/ui-kits/android-uikit-screens#user-list-screen) | UsersFragment | UsersScreenSettings | UsersViewModel | HeaderWithTextComponentUsersComponent | | [Group сhat](/ui-kits/android-uikit-screens#group-chat-screen) | GroupChatFragment | GroupChatScreenSettings | GroupChatViewModel | HeaderWithAvatarComponentMessagesComponent | | [Private chat](/ui-kits/android-uikit-screens#private-chat-screen) | PrivateChatFragment | PrivateChatScreenSettings | PrivateChatViewModel | HeaderWithAvatarComponentMessagesComponent | | [Group chat info](/ui-kits/android-uikit-screens#group-chat-info-screen) | GroupChatInfoFragment | GroupChatInfoScreenSettings | GroupChatInfoViewModel | HeaderWithTextComponentDialogInfoComponent | | [Private chat info](/ui-kits/android-uikit-screens#private-chat-info-screen) | PrivateChatInfoFragment | PrivateChatInfoScreenSettings | PrivateChatInfoViewModel | HeaderWithTextComponentDialogInfoComponent | | [Members](/ui-kits/android-uikit-screens#members-screen) | MembersFragment | MembersScreenSettings | MembersViewModel | HeaderWithIconComponentUsersComponent | | [Add members](/ui-kits/android-uikit-screens#add-members-screen) | AddMembersFragment | AddMembersScreenSettings | AddMembersViewModel | HeaderWithIconComponentUsersComponent | ## Dialog list screen The dialog list screen shows a complete list of group and private dialogs of which the current user is a member. After following the steps described in the [Get started](/ui-kits/android-uikit-send-your-first-message#get-started) section, you can display and manage the list of dialogs. <Frame> <img alt="Dialog List" /> </Frame> ### Screen structure The dialogs list screen consists of two components: header and dialogs. **Header** The header component contains a screen title and a default button in the upper right corner that allows you to select the type of dialog you want to create. The view of the header component is created and configured in the `DialogsScreenSettings`. **Dialogs** The dialogs component shows a list of all private and group dialogs that the current user is part of. To create a view for each dialog list item, you need to use the`DialogsAdapter` and bind dialog data to the view. When the current user taps on one of the dialogs in the list, they will be able to enter a [private](/ui-kits/android-uikit-screens#private-chat-screen) or [group](/ui-kits/android-uikit-screens#group-chat-screen) chat screen depending on the dialog type. ### Usage **QuickBlox UIKit for Android** provides both an **activity** and a **fragment** to create a dialog list screen. You can choose which one to build your app with, and you can solely use an **activity** or a **fragment** if you like. You can create a dialogs screen with a `DialogsActivity` which uses a `ScreenFactory` to create views. **Start an activity** You can start a dialogs activity by using the method `show()` of `DialogsActivity` as shown below: ```Kotlin Kotlin theme={null} DialogsActivity.show(context) ``` **Create a fragment** To create a `DialogsFragment` use the `ScreenFactory`, how to create a `DialogsFragment` is shown in the example below: ```Kotlin Kotlin theme={null} val fragment: Fragment = QuickBloxUiKit.getScreenFactory().createDialogs() ``` ### Customization In `DialogsScreenSettings` you can customize the `DialogsFragment` to change various aspects of the screen. The `DialogsScreenSettings` class allows you to configure `DialogsFragment` components. If you want to customize the fragment beyond using the APIs provided in the `DialogsScreenSettings`, you need to inherit the `DialogsScreenSettings` and extend the functionality. The following table shows the main classes used in the `DialogsFragment` to display and customize the dialogs screen. | Fragment | ScreenSettings | Components | Theme | RecyclerView | | --------------- | --------------------- | --------------------------------------- | ---------- | -------------- | | DialogsFragment | DialogsScreenSettings | HeaderWithIconComponentDialogsComponent | UiKitTheme | DialogsAdapter | ## Dialog name screen The dialog name screen is used in the group dialog creation flow. And allows you to set a name and photo for a group dialog. <Frame> <img alt="Dialog Name" /> </Frame> ### Screen structure The dialog name screen consists of two components: header and dialog name. **Header** The header component contains a screen title. Also, the header includes a default button "Next" in the upper right corner that allows you to open the user list screen The view of the header component is created and configured in the `DialogNameScreenSettings`. **Dialog name** The dialog name component allows you to set a name and photo for a group dialog. ### Usage **QuickBlox UIKit for Android** provides both an **activity** and a **fragment** to create a dialog name screen. You can choose which one to build your app with, and you can solely use an **activity** or a **fragment** if you like. You can create a dialog name screen with a `DialogNameActivity` which uses a `ScreenFactory` to create views. **Start an activity** You can start a dialog name activity by using the method `show()` of `DialogNameActivity` as shown below: ```Kotlin Kotlin theme={null} DialogNameActivity.show(context, dialogEntity) ``` **Create a fragment** To create a `DialogNameFragment` use the `ScreenFactory`, how to create a `DialogNameFragment` is shown in the example below: ```Kotlin Kotlin theme={null} val fragment: Fragment = QuickBloxUiKit.getScreenFactory().createDialogName(dialogEntity) ``` ### Customization In `DialogNameScreenSettings` you can customize the `DialogNameFragment` to change various aspects of the screen. The `DialogNameScreenSettings` class allows you to configure `DialogNameFragment` components. If you want to customize the fragment beyond using the APIs provided in the `DialogNameScreenSettings`, you need to inherit the `DialogNameScreenSettings` and extend the functionality. The following table shows the main classes used in the `DialogNameFragment` to display and customize the dialog name screen. | Fragment | ScreenSettings | Components | Theme | | ------------------ | ------------------------ | ------------------------------------------ | ---------- | | DialogNameFragment | DialogNameScreenSettings | HeaderWithTextComponentDialogNameComponent | UiKitTheme | ## User list screen The user list screen displays available users that can be selected to create a group or private dialog. <Frame> <img alt="User List" /> </Frame> ### Screen structure The user list screen consists of two components: header and users. **Header** The header component contains a screen title. Also, the header includes a default button "Create" in the upper right corner that allows you to create a dialog and open the chat screen. The view of the header component is created and configured in the `UsersScreenSettings`. **Users** The users component shows a list of available users that can be selected to create a group or private dialog. To create the view for each user list item, you need to use the `SelectionUsersAdapter` and bind user data to the view. ### Usage **QuickBlox UIKit for Android** provides both an **activity** and a **fragment** to create a user list screen. You can choose which one to build your app with, and you can solely use an **activity** or a **fragment** if you like. You can create a users screen with a `UsersActivity` which uses a `ScreenFactory` to create views. **Start an activity** You can start a users activity by using the method `show()` of `UsersActivity` as shown below: ```Kotlin Kotlin theme={null} UsersActivity.show(context, dialogEntity) ``` **Create a fragment** To create a `UsersFragment` use the `ScreenFactory`, how to create a `UsersFragment` is shown in the example below: ```Kotlin Kotlin theme={null} val fragment: Fragment = QuickBloxUiKit.getScreenFactory().createUsers(dialogEntity) ``` ### Customization In `UsersScreenSettings` you can customize the `UsersFragment` to change various aspects of the screen. The `UsersScreenSettings` class allows you to configure `UsersFragment` components. If you want to customize the fragment beyond using the APIs provided in the `UsersScreenSettings`, you need to inherit the `UsersScreenSettings` and extend the functionality. The following table shows the main classes used in the `UsersFragment` to display and customize the user list screen. | Fragment | ScreenSettings | Components | Theme | RecyclerView | | ------------- | ------------------- | ------------------------------------- | ---------- | --------------------- | | UsersFragment | UsersScreenSettings | HeaderWithTextComponentUsersComponent | UiKitTheme | SelectionUsersAdapter | ## Group chat screen A group chat screen allows close interactions between a limited number of users. To join the dialog, one of the members needs to add you to the dialog. <Frame> <img alt="Group Dialog" /> </Frame> ### Screen structure The group chat screen consists of two components: header and messages. **Header** The header component contains the name and the photo of the dialog. Also, the header contains a default button in the upper right corner that allows you to open the group chat info screen. The view of the header component is created and configured in the `GroupChatScreenSettings`. **Messages** The message component shows a list of all messages exchanged in the chat, in chronological order. The list displays messages sent by the current user and the other members of the dialog. The view of `MessagesComponent` is created and configured in the `GroupChatScreenSettings`. To create a view for each message list item, you need to use a `MessageAdapter`. **Send message** The send message component is where the user can either enter a text message or send the message with an attachment like file, image, or video. ### Usage **QuickBlox UIKit for Android** provides both an **activity** and a **fragment** to create a group chat screen. You can choose which one to build your app with, and you can solely use an **activity** or a **fragment** if you like. You can create a group chat screen with a `GroupChatActivity` which uses a `ScreenFactory` to create views. **Start an activity** You can start a group chat activity by using the method `show()` of `GroupChatActivity` as shown below: ```Kotlin Kotlin theme={null} GroupChatActivity.show(context, dialogId) ``` **Create a fragment** To create a `GroupChatFragment` use the `ScreenFactory`, how to create a `GroupChatFragment` is shown in the example below: ```Kotlin Kotlin theme={null} val fragment: Fragment = QuickBloxUiKit.getScreenFactory().createGroupChat(dialogId) ``` ### Customization In `GroupChatScreenSettings` you can customize the `GroupChatFragment` to change various aspects of the screen. The `GroupChatScreenSettings` class allows you to configure `GroupChatFragment` components. If you want to customize the fragment beyond using the APIs provided in the `GroupChatScreenSettings`, you need to inherit the `GroupChatScreenSettings` and extend the functionality. The following table shows the main classes used in the `PrivateChatFragment` to display and customize the group chat screen. | Fragment | ScreenSettings | Components | Theme | RecyclerView | | ----------------- | ----------------------- | ------------------------------------------ | ---------- | -------------- | | GroupChatFragment | GroupChatScreenSettings | HeaderWithAvatarComponentMessagesComponent | UiKitTheme | MessageAdapter | ## Private chat screen A private chat screen allows close interaction between two users. To participate in this type of dialog, you can create a dialog with your opponent or your opponent can create a dialog with you. <Frame> <img alt="Private Dialog" /> </Frame> ### Screen structure The private chat screen consists of two components: header and messages. **Header** The header component contains the name and the photo of the opponent. Also, the header includes a default button in the upper right corner that allows you to open the info chat screen The view of the header component is created and configured in the `PrivateChatScreenSettings`. **Messages** The message component shows a list of all messages exchanged in the chat, in chronological order. The list displays messages sent by the current user and the other member of the dialog. The view of `MessagesComponent` is created and configured in the `PrivateChatScreenSettings`. To create a view for each message list item, you need to use a `MessageAdapter`. ### Send message The send message component is where the user can enter a text message or send the message with an attachment like file, image, or video. ### Usage **QuickBlox UIKit for Android** provides both an **activity** and a **fragment** to create a private chat screen. You can choose which one to build your app with, and you can solely use an **activity** or a **fragment** if you like. You can create a private chat screen with a `PrivateChatActivity` which uses a `ScreenFactory` to create views. **Start an activity** You can start a private chat activity by using the method `show()` of `PrivateChatActivity` as shown below: ```Kotlin Kotlin theme={null} PrivateChatActivity.show(context, dialogId) ``` **Create a fragment** To create a `PrivateChatFragment` use the `ScreenFactory`, how to create a `PrivateChatFragment` is shown in the example below: ```Kotlin Kotlin theme={null} val fragment: Fragment = QuickBloxUiKit.getScreenFactory().createPrivateChat(dialogId) ``` ### Customization In `PrivateChatScreenSettings` you can customize the `PrivateChatFragment` to change various aspects of the screen. The `PrivateChatScreenSettings` class allows you to configure `PrivateChatFragment` components. If you want to customize the fragment beyond using the APIs provided in the `PrivateChatScreenSettings`, you need to inherit the `PrivateChatScreenSettings` and extend the functionality. The following table shows the main classes used in the `PrivateChatFragment` to display and customize the private chat screen. | Fragment | ScreenSettings | Components | Theme | RecyclerView | | ------------------- | ------------------------- | ------------------------------------------ | ---------- | -------------- | | PrivateChatFragment | PrivateChatScreenSettings | HeaderWithAvatarComponentMessagesComponent | UiKitTheme | MessageAdapter | ## Group chat info screen The group chat info screen allows configuring the group dialog. If you are an owner you can customize the dialog name and dialog image, you can also leave the dialog. <Frame> <img alt="Group Dialog Info" /> </Frame> ### Screen structure The group chat info screen consists of two components: header and dialog info. **Header** The header component contains a screen title. If you are a dialog owner, the header includes a default button "Edit" in the upper right corner that allows you to open the flow for updating the name and image of the dialog. The view of the header component is created and configured in the `GroupChatInfoScreenSettings`. **Dialog info** The dialog info component shows the dialog image and name by default. The view of the dialog information is created and customized in the `DialogInfoComponent` of the `GroupChatInfoScreenSettings`. ### Usage **QuickBlox UIKit for Android** provides both an **activity** and a **fragment** to create a group chat info screen. You can choose which one to build your app with, and you can solely use an **activity** or a **fragment** if you like. You can create a group chat info screen with a `GroupChatInfoActivity` which uses a `ScreenFactory` to create views. **Start an activity** You can start a group chat info activity by using the method `show()` of `GroupChatInfoActivity` as shown below: ```Kotlin Kotlin theme={null} GroupChatInfoActivity.show(context, dialogId) ``` **Create a fragment** To create a `GroupChatInfoFragment` use the `ScreenFactory`, how to create a `GroupChatInfoFragment` is shown in the example below: ```Kotlin Kotlin theme={null} val fragment: Fragment = QuickBloxUiKit.getScreenFactory().createGroupChatInfo(dialogId) ``` ### Customization In `GroupChatInfoScreenSettings` you can customize the `GroupChatInfoFragment` to change various aspects of the screen. The `GroupChatInfoScreenSettings` class allows you to configure `GroupChatInfoFragment` components. If you want to customize the fragment beyond using the APIs provided in the `GroupChatInfoScreenSettings`, you need to inherit the `GroupChatInfoScreenSettings` and extend the functionality. The following table shows the main classes used in the `GroupChatInfoFragment` to display and customize the group chat info screen. | Fragment | ScreenSettings | Components | Theme | | --------------------- | --------------------------- | ------------------------------------------ | ---------- | | GroupChatInfoFragment | GroupChatInfoScreenSettings | HeaderWithTextComponentDialogInfoComponent | UiKitTheme | ## Private chat info screen The private chat info screen allows configuring settings of the private dialog. You can also leave the dialog. <Frame> <img alt="Private Dialog Info" /> </Frame> ### Screen structure The private chat info screen consists of two components: header and dialog info. **Header** The header component contains a screen title. The view of the header component is created and configured in the `PrivateChatInfoScreenSettings`. **Dialog info** The dialog info component shows the dialog image and name by default. The view of the dialog information is created and customized in the `DialogInfoComponent` of the `PrivateChatInfoScreenSettings`. ### Usage **QuickBlox UIKit for Android** provides both an **activity** and a **fragment** to create a private chat info screen. You can choose which one to build your app with, and you can solely use an **activity** or a **fragment** if you like. You can create a private chat info screen with a `PrivateChatInfoActivity` which uses a `ScreenFactory` to create views. **Start an activity** You can start a private chat info activity by using the method `show()` of `PrivateChatInfoActivity` as shown below: ```Kotlin Kotlin theme={null} PrivateChatInfoActivity.show(context, dialogId) ``` **Create a fragment** To create a `PrivateChatInfoFragment` use the `ScreenFactory`, how to create a `PrivateChatInfoFragment` is shown in the example below: ```Kotlin Kotlin theme={null} val fragment: Fragment = QuickBloxUiKit.getScreenFactory().createPrivateChatInfo(dialogId) ``` ### Customization In `PrivateChatInfoScreenSettings` you can customize the `PrivateChatInfoFragment` to change various aspects of the screen. The `PrivateChatInfoScreenSettings` class allows you to configure `PrivateChatInfoFragment` components. If you want to customize the fragment beyond using the APIs provided in the `PrivateChatInfoScreenSettings`, you need to inherit the `PrivateChatInfoScreenSettings` and extend the functionality. The following table shows the main classes used in the `PrivateChatInfoFragment` to display and customize the private chat info screen. | Fragment | ScreenSettings | Components | Theme | | ----------------------- | ----------------------------- | ------------------------------------------ | ---------- | | PrivateChatInfoFragment | PrivateChatInfoScreenSettings | HeaderWithTextComponentDialogInfoComponent | UiKitTheme | ## Members screen A members screen is a complete list of users that are members of a group dialog. The dialog member list is created through the`MembersActivity` or `MembersFragment`. <Frame> <img alt="Members" /> </Frame> ### Screen structure The members screen consists of two components: header and users. **Header** The header component contains a screen title and a default button in the upper right corner that allows you to open add members screen. The view of the header component is created and configured in the `MembersScreenSettings`. **Users** The users component shows a list of members of a group dialog. To create the view for each user list item, you need to use the`MembersAdapter` and bind user data to the view. ### Usage **QuickBlox UIKit for Android** provides both an **activity** and a **fragment** to create a members screen. You can choose which one to build your app with, and you can solely use an **activity** or a **fragment** if you like. You can create a members screen with a `MembersActivity` which uses a `ScreenFactory` to create views. **Start an activity** You can start a members activity by using the method `show()` of `MembersActivity` as shown below: ```Kotlin Kotlin theme={null} MembersActivity.show(context, dialogId) ``` **Create a fragment** To create a `MembersFragment` use the `ScreenFactory`, how to create a `MembersFragment` is shown in the example below: ```Kotlin Kotlin theme={null} val fragment: Fragment = QuickBloxUiKit.getScreenFactory().createMembers(dialogId) ``` ### Customization In `MembersScreenSettings` you can customize the `MembersFragment` to change various aspects of the screen. The `MembersScreenSettings` class allows you to configure `MembersFragment` components. If you want to customize the fragment beyond using the APIs provided in the `MembersScreenSettings`, you need to inherit the `MembersScreenSettings` and extend the functionality. The following table shows the main classes used in the `MembersFragment` to display and customize the members screen. | Fragment | ScreenSettings | Components | Theme | RecyclerView | | --------------- | --------------------- | ------------------------------------- | ---------- | -------------- | | MembersFragment | MembersScreenSettings | HeaderWithIconComponentUsersComponent | UiKitTheme | MembersAdapter | ## Add members screen The add members screen allows adding new members to a group dialog via the `AddMembersActivity` or `AddMembersFragment`. All users are listed through the pagination, and members who already belong to the dialog are not shown. <Frame> <img alt="Add Members" /> </Frame> ### Screen structure The add members screen consists of two components: header and users **Header** The header component contains a screen title. The view of the header component is created and configured in the `AddMembersScreenSettings`. **Users** The users component shows a list of users that can be added to a group dialog. To create the view for each user list item, you need to use the`AddMembersAdapter` and bind user data to the view. ### Usage **QuickBlox UIKit for Android** provides both an **activity** and a **fragment** to create a add members screen. You can choose which one to build your app with, and you can solely use an **activity** or a **fragment** if you like. You can create a add members screen with a `AddMembersActivity` which uses a `ScreenFactory` to create views. **Start an activity** You can start a add members activity by using the method `show()` of `AddMembersActivity` as shown below: ```Kotlin Kotlin theme={null} AddMembersActivity.show(context, dialogId) ``` **Create a fragment** To create a `AddMembersFragment` use the `ScreenFactory`, how to create a `AddMembersFragment` is shown in the example below: ```Kotlin Kotlin theme={null} val fragment: Fragment = QuickBloxUiKit.getScreenFactory().createAddMembers(dialogId) ``` ### Customization In `AddMembersScreenSettings` you can customize the `AddMembersFragment` to change various aspects of the screen. The `AddMembersScreenSettings` class allows you to configure `AddMembersFragment` components. If you want to customize the fragment beyond using the APIs provided in the `AddMembersScreenSettings`, you need to inherit the `AddMembersScreenSettings` and extend the functionality. The following table shows the main classes used in the `AddMembersFragment` to display and customize the add members screen. | Fragment | ScreenSettings | Components | Theme | RecyclerView | | ------------------ | ------------------------ | ------------------------------------- | ---------- | ----------------- | | AddMembersFragment | AddMembersScreenSettings | HeaderWithIconComponentUsersComponent | UiKitTheme | AddMembersAdapter | # Send your first message Source: https://docs.quickblox.com/ui-kits/android-uikit-send-your-first-message The **QuickBlox UIKit for Android** comprises a collection of pre-assembled UI components that enable the effortless creation of an in-app chat equipped with all the necessary messaging functionalities. Our development kit encompasses light and dark themes, colors, and various other features. These components can be personalized to fashion an engaging messaging interface that reflects your brand's distinct identity. The QuickBlox UIKit fully supports both private and group dialogs. To initiate the process of sending a message from the ground up using Java or Kotlin, please refer to the instructions provided in the guide below. ## Requirements The minimum requirements for QuickBlox UIKit for Android are: * Android 5.0 (API level 21) or higher * Java 8 or higher * Android Gradle plugin 4.0.1 or higher ## Before you begin Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. Create the app clicking New app button. Configure the app. Type in the information about your organization into corresponding fields and click Add button. Go to Dashboard => YOUR\_APP => Overview section and copy your Application ID, Authorization Key, Authorization Secret, and Account Key . ## Get started If you build mobile applications for the Android platform then you are no doubt already familiar with [Android Studio](https://developer.android.com/studio), a popular tool and the official Integrated Development Environment (IDE) for Android app development. ### Step 1. Create a project 1. Launch Android Studio 2. Click **"New project"** button in the Welcome to Android Studio window. 3. Select Empty Activity in **“Phone and Tablet”** Template window and click Next. 4. Set your project name and desired configurations and click **Finish**. <Frame> <img alt="Image 1" /> </Frame> ### Step 2. Install QuickBlox UIKit There are 2 ways to install to QuickBlox UIKit from: * Repository * Local source **Install QuickBlox UIKit from repository** To install **QuickBlox UIKit** to your app, import **QuickBlox UIKit** and **QuickBlox SDK** dependencies via build.gradle file. Include a reference to the SDK repository in your **project-level build.gradle** file at the root directory or to **settings.gradle** file. Specify the URL of the QuickBlox repository where the files are stored. Following this URL, gradle finds SDK artifacts. ```Kotlin Kotlin theme={null} repositories { maven { url "https://github.com/QuickBlox/android-ui-kit-releases/raw/master/" } maven { url "https://github.com/QuickBlox/quickblox-android-sdk-releases/raw/master/" } } ``` Then need to add the implementation of **QuickBlox UIKit** and **QuickBlox SDK** to dependencies in your **module-level(App) build.gradle** file. ``` dependencies { implementation "com.quickblox:android-ui-kit:0.10.1" implementation 'com.quickblox:quickblox-android-sdk-messages:4.3.0' implementation 'com.quickblox:quickblox-android-sdk-chat:4.3.0' implementation 'com.quickblox:quickblox-android-sdk-content:4.3.0' } ``` **Install QuickBlox UIKit from local source** To connect **QuickBlox SDK** to your app, import **QuickBlox SDK** dependencies via **build.gradle** file. Include a reference to the SDK repository in your **project-level build.gradle** file at the root directory or to **settings.gradle** file. Specify the URL of the QuickBlox repository where the files are stored. Following this URL, gradle finds SDK artifacts. ```Kotlin Kotlin theme={null} repositories { google() mavenCentral() maven { url "https://github.com/QuickBlox/quickblox-android-sdk-releases/raw/master/" } } ``` Then you need to download the source code of **Android UIKit** from the GitHub repository at [this link](https://github.com/QuickBlox/android-ui-kit) to include UIKit locally in your project. Specify the path of the UIKit project in **settings.gradle** file. ```Kotlin Kotlin theme={null} include ':ui-kit' project(':ui-kit').projectDir = new File('YourFullPathToDir/android-ui-kit/ui-kit') ``` Also, need to add the implementation of the UIKit project to dependencies in your **module-level(App) build.gradle** file. ```Kotlin Kotlin theme={null} dependencies { implementation project(':ui-kit') } ``` ### Step 3. Init QuickBlox SDK To initialize the **QuickBlox Android SDK**, we need to create a class for example name **App** and perform the initialization in the **onCreate()** method. The class App will inherit from the [Application](https://developer.android.comhttps://docs.quickblox.com/reference/android/app/Application) class to ensure proper management of the application's lifecycle and provide a global context. The Application class is the base class for creating Android applications and serves as a container for global application states and settings. An application can have only one instance of the Application class, which is created by the system when the application is launched. By inheriting from the Application class, we can create a custom class App that extends the functionality of the base class. In the App class, we can override the onCreate() method, which is called when the application is launched, and perform the initialization of the **QuickBlox Android SDK** there. Thus, the App class becomes the entry point and control point for initializing and configuring various components, including the QuickBlox SDK, during the application's startup. Also to init *QuickBlox SDK* you need to pass Application ID, Authorization Key, Authorization Secret, and Account Key to the init() method. How to get credentials is described in the [Before you begin](https://dash.readme.com/project/quickbloxdoc/v1.7/docs/send-your-first-message#before-you-begin) section. ```Kotlin Kotlin theme={null} private const val APPLICATION_ID = "67895" private const val AUTH_KEY = "lkjdueksu7392kj" private const val AUTH_SECRET = "BTFsj7Rtt27DAmT" private const val ACCOUNT_KEY = "9yvTe17TmjNPqDoYtfqp" class App : Application() { override fun onCreate() { super.onCreate() QBSDK.init(applicationContext, APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY) } } ``` <Warning> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend. Instead you can [Initialize QuickBlox SDK without Authorization Key and Secret](/sdks/android-setup#initialize-quickblox-sdk-without-authorization-key-and-secret) </Warning> App class needs to be registered in the AndroidManifest.xml file. ```XML XML theme={null} <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:name=".App" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.QuickBloxUiKitApplication"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> ``` ### Step 4. Authenticate and start QuickBlox UIKit Before sending your first message you need to authenticate users in the **QuickBlox** system. You can read more about different ways of authentication by [this link](/sdks/android-authentication). In our example, we show how to authenticate users with login and password. After successfully **sign-in**, you need to initialize the **QuickBlox UIKit** by invoking **init(applicationContext)** method of the **QuickBlox UIKit** and start the Dialogs screen by invoking **show()** method of the **DialogActivity** from your Activity. ```Kotlin Kotlin theme={null} class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val user = QBUser() user.login = "userlogin" user.password = "userpassword" QBUsers.signIn(user).performAsync(object : QBEntityCallback<QBUser> { override fun onSuccess(user: QBUser?, bundle: Bundle?) { // init QuickBlox UIKit QuickBloxUiKit.init(applicationContext) // show Dialogs screen DialogsActivity.show(this@MainActivity) } override fun onError(exception: QBResponseException?) { // handle exception } }) } } ``` ### Step 5. Send your first message After successful authentication, you will be directed to the chat list screen. To send your first message, follow these steps: * Tap on the icon located in the top-right corner of the screen, specifically designed for creating a new chat. * Choose the type of chat you want to create. You can select either a private chat or a group chat. * If you selected a group chat, enter a name for the chat. * Select the users with whom you want to create the chat and tap the "Create" button. * After creating the chat, enter your first message and start sending it. Now, your first message will be sent to the created chat and will be visible to other users who have joined the chat. <Tip> Quick start using our [Android UIKit sample](https://github.com/QuickBlox/quickblox-android-sdk/tree/master/android-ui-kit-sample) This sample implements authorization functionality and provides an example of color theme customization. Sample code available by this [link](https://github.com/QuickBlox/quickblox-android-sdk/tree/master/android-ui-kit-sample). How to set up and run a sample, see the article in out blog [Getting Started with the QuickBlox Android UIKit](https://quickblox.com/blog/getting-started-with-quickblox-android-ui-kit/). </Tip> # UIKit Source: https://docs.quickblox.com/ui-kits/flutter-uikit ## Coming soon # AI Features Source: https://docs.quickblox.com/ui-kits/ios-uikit-ai-features QuickBloxUIKit comes with a range of AI features that enhance the capabilities of your chat application. ## Overview These AI features leverage cutting-edge technologies to assist users, translate messages, summarize content, and more. | Feature | Group Dialog | Private Dialog | | ------------- | ------------ | -------------- | | Assist Answer | ✓ | ✓ | | Translate | ✓ | ✓ | | Rephrase | ✓ | ✓ | ## Assist Answer **QuickBlox iOS UIKit** provides answer assistant functionality that helps users effortlessly send various answers considering chat history. <img /> ### How to use In iOS UIKit version 0.4.0, you should enable QuickBlox AI Answer Assistant using your Smart ChatAssist ID from the QuickBlox admin panel. #### Enable AI Extensions 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Extensions** page 2. Select the checkboxes for the features you want to enable. 3. Click the Save button to save changes. <img alt="Robot Icon" /> #### Review your AI Agent To get **ID** of AI Agent to use it as `smartChatAssistantId ` param follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Agent** page 2. Choose the ID of the AI Agent you want to update and click on it. 3. Edit the AI Agent settings. 4. Click the Save button to save changes. <img alt="Robot Icon" /> #### Enable with AI Agent ID ```Swift Swift theme={null} let smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" QuickBloxUIKit.feature.ai.smartChatAssistantId = smartChatAssistantId QuickBloxUIKit.feature.ai.answerAssist.enable = true ``` | Parameter | Type | Description | | -------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual AI Agent ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | When enabled, a button will appear next to each incoming message in the chat interface. <img alt="Robot Icon" /> When the button is clicked, the Assist Answer feature will be launched, and a response will be generated based on the chat history. <img alt="Robot Icon" /> iOS QuickBlox UIKit allows customization of the “AI Assist Answer” button title by modifying the property: ```swift Swift theme={null} QuickBloxUIKit.feature.ai.ui.answerAssist.title = "Your Custom Title" ``` More examples of setting custom settings for the appearance of user interface elements from a user application can be found in our [UIKitSample](https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-ui-kit). #### Disable Assist Answer ```swift Swift theme={null} QuickBloxUIKit.feature.ai.answerAssist.enable = false ``` #### Enable with Open AI Token or Proxy server <Tip> **Recommendation** The code snippets below are not recommended for use with iOS UIKit version 0.4.0 and above, as they will be deprecated. In the latest version of our iOS UIKit, AI Assist customizations are no longer supported. </Tip> [Video tutorial](https://www.youtube.com/watch?v=1HaTipnH2VY). To use the AI Assist Answer feature in your QuickBloxUIKit project, follow these steps: 1. Enable the AI Assist Answer feature: ```swift Swift theme={null} QuickBloxUIKit.feature.ai.answerAssist.enable = true ``` 2. Set up the AI settings by providing either the OpenAI [API key](https://platform.openai.com/account/api-keys): ```swift Swift theme={null} QuickBloxUIKit.feature.ai.answerAssist.apiKey = "YOUR_OPENAI_API_KEY" ``` Or set up with a proxy server: ```Swift Swift theme={null} QuickBloxUIKit.feature.ai.answerAssist.serverPath = "https://your-proxy-server-url" ``` <Note> We recommend using a proxy server like the [QuickBlox AI Assistant Proxy Server](https://github.com/QuickBlox/qb-ai-assistant-proxy-server) offers significant benefits in terms of security and functionality: * When making direct requests to the OpenAI API from the client-side, sensitive information like API keys may be exposed. By using a proxy server, the API keys are securely stored on the server-side, reducing the risk of unauthorized access or potential breaches. * The proxy server can implement access control mechanisms, ensuring that only authenticated and authorized users with valid QuickBlox user tokens can access the OpenAI API. This adds an extra layer of security to the communication. </Note> 3. A developer using the AI Answer Assist library has the ability use to Default AIAnswerAssistSettings. ```Swift Swift theme={null} public class AIAnswerAssistSettings { /// Determines if assist answer functionality is enabled. public var enable: Bool = true /// The OpenAI API key for direct API requests (if not using a proxy server). public var apiKey: String = "" /// The URL path of the proxy server for more secure communication (if not using the API key directly). /// [QuickBlox AI Assistant Proxy Server](https://github.com/QuickBlox/qb-ai-assistant-proxy-server). public var serverPath: String = "" /// Represents the available API versions for OpenAI. public var apiVersion: QBAIAnswerAssistant.APIVersion = .v1 /// Optional organization information for OpenAI requests. public var organization: String? = nil /// Represents the available GPT models for OpenAI. public var model: QBAIAnswerAssistant.Model = .gpt3_5_turbo /// The temperature setting for generating responses (higher values make output more random). public var temperature: Float = 0.5 /// The maximum number of tokens to generate in the request. public var maxRequestTokens: Int = 3000 /// The maximum number of tokens to generate in the response. public var maxResponseTokens: Int? = nil } ``` 4. A developer using the AI Answer Assist library has the ability to setup custom settings. This is an example of creating custom tones and installing them in QuickBlox iOS UIKit from the custom application. ```Swift Swift theme={null} import QBAIAnswerAssistant // Setup custom settings for QBAIAnswerAssistant. QuickBloxUIKit.feature.ai.answerAssist.organization = "CustomDev" QuickBloxUIKit.feature.ai.answerAssist.model = .gpt4 QuickBloxUIKit.feature.ai.answerAssist.temperature = 0.8 QuickBloxUIKit.feature.ai.answerAssist.maxRequestTokens = 3500 ``` ## Translate **QuickBlox iOS UIKit** provides Translate functionality that helps users simplify cross-lingual communication by providing instant message translation services. <img /> ### How to Use In iOS UIKit version 0.4.0, you should enable QuickBlox AI Translate using your Smart ChatAssist ID from the QuickBlox admin panel. #### Enable AI Extensions 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Extensions** page 2. Select the checkboxes for the features you want to enable. 3. Click the Save button to save changes. <img alt="Robot Icon" /> #### Review your AI Agent To get **ID** of AI Agent to use it as `smartChatAssistantId ` param follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Agent** page 2. Choose the ID of the AI Agent you want to update and click on it. 3. Edit the AI Agent settings. 4. Click the Save button to save changes. <img alt="Robot Icon" /> #### Enable with AI Agent ID ```swift Swift theme={null} let smartChatAssistantId = "XXXXXXXXXXXXXXXXXXXXXXXX" QuickBloxUIKit.feature.ai.smartChatAssistantId = smartChatAssistantId QuickBloxUIKit.feature.ai.translate.enable = true ``` | Parameter | Type | Description | | :------------------- | :----- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- | | smartChatAssistantId | String | This field should hold your actual AI Agent ID that you'll receive from the QuickBlox account. This ID is used to authenticate your requests to the AI service. | When enabled, the "Show translation" button will be displayed at the bottom of every incoming message in the chat interface. <img /> When you click on it, this message will be translated into the language set for translation, and the "Show original" button will be displayed, when clicked, the text of the message will immediately take the original version. <img /> iOS QuickBlox UIKit allows customization for the translate feature to adjust the labels for showing the original text and the translated text. ```swift Swift theme={null} QuickBloxUIKit.feature.ai.ui.translate.showOriginal = "Original" QuickBloxUIKit.feature.ai.ui.translate.showTranslation = "Translation" ``` More examples of setting custom settings for the appearance of user interface elements from a user application can be found in our [UIKitSample](https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-ui-kit). #### Disable AI Translate ```swift theme={null} QuickBloxUIKit.feature.ai.translate.enable = false ``` #### Enable with Open AI Token or Proxy server <Tip> **Recommendation** The code snippets below are not recommended for use with iOS UIKit version 0.4.0 and above, as they will be deprecated. In the latest version of our iOS UIKit, AI Translate customizations are no longer supported. </Tip> To use the AI Translate feature in your QuickBloxUIKit project, follow these steps: 1. Enable the AI Translate feature: ```swift Swift theme={null} QuickBloxUIKit.feature.ai.translate.enable = true ``` 2. Set up the AI settings by providing either the OpenAI [API key](https://platform.openai.com/account/api-keys): ```swift Swift theme={null} QuickBloxUIKit.feature.ai.translate.apiKey = "YOUR_OPENAI_API_KEY" ``` Or set up with a proxy server: ```Swift Swift theme={null} QuickBloxUIKit.feature.ai.translate.serverPath = "https://your-proxy-server-url" ``` <Note> We recommend using a proxy server like the [QuickBlox AI Assistant Proxy Server](https://github.com/QuickBlox/qb-ai-assistant-proxy-server) offers significant benefits in terms of security and functionality: * When making direct requests to the OpenAI API from the client-side, sensitive information like API keys may be exposed. By using a proxy server, the API keys are securely stored on the server-side, reducing the risk of unauthorized access or potential breaches. * The proxy server can implement access control mechanisms, ensuring that only authenticated and authorized users with valid QuickBlox user tokens can access the OpenAI API. This adds an extra layer of security to the communication. </Note> 3. A developer using the AI Translate library has the ability use to Default `QBAITranslate.Language` and Default AITranslateSettings. ```Swift Swift theme={null} public class AITranslateSettings { /// The current `QBAITranslate.Language`. /// /// Default the same as system language or `.english` if `QBAITranslate.Language` is not support system language. public var language: QBAITranslate.Language /// Determines if assist answer functionality is enabled. public var enable: Bool = true /// The OpenAI API key for direct API requests (if not using a proxy server). public var apiKey: String = "" /// The URL path of the proxy server for more secure communication (if not using the API key directly). /// [QuickBlox AI Assistant Proxy Server](https://github.com/QuickBlox/qb-ai-assistant-proxy-server). public var serverPath: String = "" /// Represents the available API versions for OpenAI. public var apiVersion: QBAITranslate.APIVersion = .v1 /// Optional organization information for OpenAI requests. public var organization: String? = nil /// Represents the available GPT models for OpenAI. public var model: QBAITranslate.Model = .gpt3_5_turbo /// The temperature setting for generating responses (higher values make output more random). public var temperature: Float = 0.5 /// The maximum number of tokens to generate in the request. public var maxRequestTokens: Int = 3000 /// The maximum number of tokens to generate in the response. public var maxResponseTokens: Int? = nil } ``` 4. A developer using the AI Translate library has the ability to setup custom translation language (by default used system language). Also a developer has the ability to setup custom settings. This is an example of creating custom tones and installing them in QuickBlox iOS UIKit from the custom application. ```Swift Swift theme={null} import QBAITranslate // Set up the language for translation(by default used system language) QuickBloxUIKit.feature.ai.translate.language = .spanish // Setup custom settings for Translate. QuickBloxUIKit.feature.ai.translate.organization = "CustomDev" QuickBloxUIKit.feature.ai.translate.model = .gpt4 QuickBloxUIKit.feature.ai.translate.temperature = 0.8 QuickBloxUIKit.feature.ai.translate.maxRequestTokens = 3500 ``` Incorporate AI Translate into your iOS chat application seamlessly using the QBAITranslate Swift package. Unlock the potential of AI-driven interactions and provide your users with real-time translation capabilities, enhancing user communication and experience. For additional resources, explore the QuickBlox AI Assistant Proxy Server and the QBAITranslate Swift package repositories. Resources: * [QBAIProxy Server](https://github.com/QuickBlox/qb-ai-assistant-proxy-server) * [QBAITranslate Swift Package](https://github.com/QuickBlox/QBAITranslate) ## Rephrase The AI Rephrase feature in QuickBloxUIKit empowers you to seamlessly integrate AI Rephrase capabilities into your chat-based application using the [QBAIRephrase Swift package](https://github.com/QuickBlox/ios-ai-rephrase). This feature leverages the OpenAI [API key](https://platform.openai.com/account/api-keys) or [proxy server](https://github.com/QuickBlox/qb-ai-assistant-proxy-server) to generate responses more securely and efficiently. <img /> The AI Rephrase library allows you to rephrase a message using different tones so that the user can communicate effectively in different situations. ### How to Use To use the AI Rephrase feature in your QuickBloxUIKit project, follow these steps: 1. Enable the AI Rephrase feature: ```Swift swift theme={null} QuickBloxUIKit.feature.ai.rephrase.enable = true ``` If this option is enabled, the user interface provides a clear option or menu that allows the user to select the desired tone. <img /> Once a tone is selected, the message is rephrased to reflect the characteristics of the selected tone. A paraphrased message retains the main purpose of the original message. <img /> Users have the ability to seamlessly switch between different tones without having to rewrite the original message. <img /> After rephrasing a message, the user is given an easily accessible option to return to the original. Clicking on this "Back to original" option will instantly restore the message to its original state. <img /> 2. Set up the AI settings by providing either the OpenAI [API key](https://platform.openai.com/account/api-keys): ```Swift swift theme={null} QuickBloxUIKit.feature.ai.rephrase.apiKey = "YOUR_OPENAI_API_KEY" ``` Or set up with a proxy server: ```Swift Swift theme={null} QuickBloxUIKit.feature.ai.rephrase.serverPath = "https://your-proxy-server-url" ``` <Tip> We recommend using a proxy server like the [QuickBlox AI Assistant Proxy Server](https://github.com/QuickBlox/qb-ai-assistant-proxy-server) offers significant benefits in terms of security and functionality: * When making direct requests to the OpenAI API from the client-side, sensitive information like API keys may be exposed. By using a proxy server, the API keys are securely stored on the server-side, reducing the risk of unauthorized access or potential breaches. * The proxy server can implement access control mechanisms, ensuring that only authenticated and authorized users with valid QuickBlox user tokens can access the OpenAI API. This adds an extra layer of security to the communication. </Tip> 3. A developer using the AI Rephrase library has the ability use to Default Tones and Default AIRephraseSettings. ```Swift Swift theme={null} public class AIRephraseSettings { public var tones: [QBAIRephrase.AITone] = [ QBAIRephrase.AITone.professional, QBAIRephrase.AITone.friendly, QBAIRephrase.AITone.encouraging, QBAIRephrase.AITone.empathetic, QBAIRephrase.AITone.neutral, QBAIRephrase.AITone.assertive, QBAIRephrase.AITone.instructive, QBAIRephrase.AITone.persuasive, QBAIRephrase.AITone.sarcastic, QBAIRephrase.AITone.poetic ] /// Determines if assist answer functionality is enabled. public var enable: Bool = true /// The OpenAI API key for direct API requests (if not using a proxy server). public var apiKey: String = "" /// The URL path of the proxy server for more secure communication (if not using the API key directly). /// [QuickBlox AI Assistant Proxy Server](https://github.com/QuickBlox/qb-ai-assistant-proxy-server). public var serverPath: String = "" /// Represents the available API versions for OpenAI. public var apiVersion: QBAIRephrase.APIVersion = .v1 /// Optional organization information for OpenAI requests. public var organization: String? = nil /// Represents the available GPT models for OpenAI. public var model: QBAIRephrase.Model = .gpt3_5_turbo /// The temperature setting for generating responses (higher values make output more random). public var temperature: Float = 0.5 /// The maximum number of tokens to generate in the request. public var maxRequestTokens: Int = 3000 /// The maximum number of tokens to generate in the response. public var maxResponseTokens: Int? = nil } ``` 4. A developer using the AI Rephrase library has the ability to delete tones, create his own tones, and add them to tailor the user interface to his needs. Also a developer has the ability to setup custom settings. This is an example of creating custom tones and installing them in QuickBlox iOS UIKit from the custom application. ```Swift Swift theme={null} import QBAIRephrase // Custom Tones public extension QBAIRephrase.AITone { static let youth = QBAIRephrase.AITone ( name: "Youth", description: "This will allow you to edit messages so that they sound youthful and less formal, using youth slang vocabulary that includes juvenile expressions, unclear sentence structure and without maintaining a formal tone. This will avoid formal speech and ensure appropriate youth greetings and signatures.", icon: "🛹" ) static let doctor = QBAIRephrase.AITone ( name: "Doctor", description: "This will allow you to edit messages so that they sound doctoral, using medical and medical vocabulary, including professional expressions, unclear sentence structure. This will allow you to make speeches in a medical-doctoral tone and provide appropriate medical greetings and signatures.", icon: "🩺" ) } // Array of required tones for your application. public var customTones: [QBAIRephrase.AITone] = [ .youth, // Custom Tone .doctor, // Custom Tone .sarcastic, // Default Tone .friendly, // Default Tone .empathetic, // Default Tone .neutral, // Default Tone .poetic // Default Tone ] // Setup an array of required tones for your application. QuickBloxUIKit.feature.ai.rephrase.tones = customTones // Setup custom settings for Rephrase. QuickBloxUIKit.feature.ai.rephrase.organization = "CustomDev" QuickBloxUIKit.feature.ai.rephrase.model = .gpt4 QuickBloxUIKit.feature.ai.rephrase.temperature = 0.8 QuickBloxUIKit.feature.ai.rephrase.maxRequestTokens = 3500 ``` More examples of setting custom settings for the appearance of user interface elements from a user application can be found in our [UIKitSample](https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-ui-kit). 5. A developer using the AI Rephrase library has the ability to customize the Appearance of UI Rephrase elements to adapt the user interface to their needs. ```Swift Swift theme={null} // Default UI Settings for Rephrase public struct AIRephraseUISettings { public var nameForeground: Color public var nameFont: Font public var iconFont: Font public var bubbleBackground: Color public var bubbleRadius: CGFloat = 12.5 public var contentSpacing: CGFloat = 4.0 public var height: CGFloat = 25.0 public var buttonHeight: CGFloat = 38.0 public var contentPadding: EdgeInsets = EdgeInsets(top: 6, leading: 4, bottom: 6, trailing: 4) public var bubblePadding: EdgeInsets = EdgeInsets(top: 2, leading: 8, bottom: 2, trailing: 8) public init(_ theme: ThemeProtocol) { self.nameForeground = theme.color.mainText self.nameFont = theme.font.callout self.iconFont = theme.font.caption self.bubbleBackground = theme.color.outgoingBackground } } ``` This is an example of setting custom settings for the appearance of UI elements from a custom application. ```Swift swift theme={null} QuickBloxUIKit.feature.ai.ui.rephrase.bubbleBackground = .green QuickBloxUIKit.feature.ai.ui.rephrase.nameForeground = .red ``` More examples of setting custom settings for the appearance of user interface elements from a user application can be found in our [UIKitSample](https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-ui-kit). Incorporate AI Rephrase into your iOS chat application seamlessly using the QBAIRephrase Swift package. Unlock the potential of AI-driven interactions and provide your users with real-time translation capabilities, enhancing user communication and experience. For additional resources, explore the QuickBlox AI Assistant Proxy Server and the QBAIRephrase Swift package repositories. Resources: * [QBAIProxy Server](https://github.com/QuickBlox/qb-ai-assistant-proxy-server) * [QBAIRephrase Swift package](https://github.com/QuickBlox/QBAITranslate) * [QuickBlox Documentation](https://quickblox.com/documentation/ios/ai-features) ### Default tones * **Professional tone:** This will allow you to edit messages to sound more formal, using technical language, clear sentence structures, and maintaining a respectful tone. This would avoid colloquial language and ensure appropriate greetings and signatures. * **Friendly Tone:** This will allow you to tailor your messages to reflect a casual, friendly tone. It will include casual language, use emoticons, exclamation points and other informal elements to make the message seem more friendly and approachable. * **Encouraging tone**: This tone will be useful for motivation and encouragement. It will include positive words, affirmations and express support and faith in the recipient. * **Empathic Tone:** This tone will be used to show understanding and empathy. This will require softer language, acknowledgment of feelings, and demonstrations of compassion and support. * **Neutral Tone:** For when you want to maintain an even, impartial and objective tone. He will avoid harsh statements and emotional words, preferring clear and direct communication. * **Assertive Tone**: This tone is useful for making a clear statement, asserting a position, or in negotiations. He uses direct speech, is confident and does not soften his words. * **Instructive Tone:** This tone would be useful for tutorials, guides, or other teaching and learning materials. It is clear, concise and logically guides the reader through the steps or processes. * **Persuasive Tone:** This tone can be used when trying to convince someone or to argue your point. He uses persuasive language, strong words and logical reasoning. * **Sarcastic/Ironic Tone:** This tone can make the communication more humorous or convey an ironic attitude. It is more difficult to implement because it requires the AI to understand the nuances of the language and may not always be perceived by the reader as intended. * **Poetic Tone:** This will add an artistic touch to messages by using figurative language, rhyme, and rhythm to create more expressive text. # Customization Source: https://docs.quickblox.com/ui-kits/ios-uikit-customization The QuickBlox UIKit for iOS allows you to create your own unique view of the UIKit. ## Default theme The QuickBlox iOS UIKit comes with the default Theme conforms toThemeProtocol out of the box. It consists of a ThemeColor, ThemeFont, ThemeImage and an ThemeString that describe the properties needed to properly display the appearance of the kit. ## Color Theme <Frame> <img alt="Color Theme" /> </Frame> <Frame> <img alt="iOS Doc Light" /> </Frame> <Frame> <img alt="iOS Doc Dark" /> </Frame> The default ColorTheme conforms to the ThemeColorProtocol: ```Swift Swift theme={null} public protocol ThemeColorProtocol { var mainElements: Color { get set } var secondaryElements: Color { get set } var tertiaryElements: Color { get set } var disabledElements: Color { get set } var mainText: Color { get set } var secondaryText: Color { get set } var caption: Color { get set } var mainBackground: Color { get set } var secondaryBackground: Color { get set } var tertiaryBackground: Color { get set } var incomingBackground: Color { get set } var outgoingBackground: Color { get set } var dropdownBackground: Color { get set } var inputBackground: Color { get set } var divider: Color { get set } var error: Color { get set } var success: Color { get set } var highLight: Color { get set } var system: Color { get set } } ``` which describes the required a color set to properly display the user interface: ```Swift Swift theme={null} class ThemeColor: QuickBloxUIKit.ThemeColorProtocol { var mainElements: Color = Color("MainElements") var secondaryElements: Color = Color("SecondaryElements") var tertiaryElements: Color = Color("TertiaryElements") var disabledElements: Color = Color("DisabledElements") var mainText: Color = Color("MainText") var secondaryText: Color = Color("SecondaryText") var caption: Color = Color("Caption") var mainBackground: Color = Color("MainBackground") var secondaryBackground: Color = Color("SecondaryBackground") var tertiaryBackground: Color = Color("TertiaryBackground") var incomingBackground: Color = Color("IncomingBackground") var outgoingBackground: Color = Color("OutgoingBackground") var dropdownBackground: Color = Color("DropdownBackground") var inputBackground: Color = Color("InputBackground") var divider: Color = Color("Divider") var error: Color = Color("Error") var success: Color = Color("Success") var highLight: Color = Color("HighLight") var system: Color = Color("System") init() {} } ``` This theme supports Light and Dark schemes configured with ColorSet in Xcode: <img alt="" /> If you are not yet using Color Sets in your application, you can get acquainted with them in the documentation from [Apple documentation](https://developer.apple.com/documentation/uikit/appearance%5Fcustomization/supporting%5Fdark%5Fmode%5Fin%5Fyour%5Finterface) ### Use your own Color Theme You can create and use your own color theme. To do this you need to create a class that implements `QuickBloxUIKit\.ThemeColorProtocol` and initialize it with your own color set. The example shows different options for color constructors. Use the most suitable for you: ```Swift Swift theme={null} class YourAmazingColorTheme: ThemeColorProtocol { // 1. Init with setup Light and Dark schemes. var mainElements: Color = Color(uiColor: UIColor { traitCollection in traitCollection.userInterfaceStyle == .dark ? UIColor.blue : UIColor.cyan }) // 2. Some convenience methods to create colors. var secondaryElements: Color = .gray var tertiaryElements: Color = .black var disabledElements: Color = .black.opacity(0.5) // 3. Creates a color from a color set that you indicate by name with setup Light and Dark schemes. var mainText: Color = Color("Your_MainText") var secondaryText: Color = Color("Your_SecondaryText") var caption: Color = Color("Your_Caption") var mainBackground: Color = Color("Your_MainBackground") var secondaryBackground: Color = Color("Your_SecondaryBackground") var tertiaryBackground: Color = Color("Your_TertiaryBackground") var incomingBackground: Color = Color("Your_IncomingBackground") var outgoingBackground: Color = Color("Your_OutgoingBackground") var dropdownBackground: Color = Color("Your_DropdownBackground") var inputBackground: Color = Color("Your_InputBackground") var divider: Color = Color("Your_Divider") var error: Color = Color("Your_Error") var success: Color = Color("Your_Success") var highLight: Color = Color("Your_HighLight") var system: Color = Color("Your_System") init() {} } ``` Now that you've successfully created your color theme, it's time to apply it to QuickBlox iOS UIKit's screens. To do this, let's create a YourAmazingTheme class corresponding to `QuickBloxUIKit.ThemeProtocol`, which obliges to do initialization with ThemeColorProtocol, ThemeFontProtocol, ThemeImageProtocol and ThemeStringProtocol: ```Swift Swift theme={null} class YourAmazingTheme: ThemeProtocol { var color: ThemeColorProtocol var font: ThemeFontProtocol var image: ThemeImageProtocol var string: ThemeStringProtocol init(color: ThemeColorProtocol, font: ThemeFontProtocol, image: ThemeImageProtocol) { self.color = color self.font = font self.image = image self.string = string } } ``` Next, we create an object of this class: ```Swift Swift theme={null} var appTheme: YourAmazingTheme = YourAmazingTheme(color: YourAmazingColorTheme(), font: QuickBloxUIKit.ThemeFont(), image: QuickBloxUIKit.ThemeImage(), string: QuickBloxUIKit.ThemeString()) ``` Now it remains to install your theme. You can do this in a place convenient for you in your application, for example, at the entry point to the QuickBlox iOS UIKit: ```Swift Swift theme={null} import SwiftUI import QuickBloxUIKit struct ShowQuickBlox: View { init() { var appTheme: YourAmazingTheme = YourAmazingTheme(color: MyAmazingColorTheme(), font: QuickBloxUIKit.ThemeFont(), image: QuickBloxUIKit.ThemeImage(), string: QuickBloxUIKit.ThemeString()) QuickBloxUIKit.settings.theme = appTheme // Custom color theme setting. } var body: some View { QuickBloxUIKit.dialogsView(onExit: { // Handling an event when exiting the QuickBloxUIKit for example, disconnect and logout }) } } ``` ## Font Theme The default FontTheme conforms to the ThemeFontProtocol: ```Swift Swift theme={null} public protocol ThemeFontProtocol { var headline: Font { get set } var footnote: Font { get set } var caption: Font { get set } var caption2: Font { get set } var callout: Font { get set } var largeTitle: Font { get set } var title1: Font { get set } var title3: Font { get set } } ``` with such default fonts: ```Swift Swift theme={null} public class ThemeFont: ThemeFontProtocol { public var headline: Font = .headline public var footnote: Font = .footnote.weight(.semibold) public var caption: Font = .caption public var caption2: Font = .caption2 public var callout: Font = .callout public var largeTitle: Font = .largeTitle public var title1: Font = .title.weight(.semibold) public var title3: Font = .title3 public init() {} } ``` ### Use your own Font Theme You can create and use your own font theme. To do this you need to create a class that implements `QuickBloxUIKit.ThemeFontProtocol` with the fonts you need. For example: ```Swift Swift theme={null} public class CustomThemeFont: ThemeFontProtocol { public var headline: Font = .custom("Menlo", size: 17) public var footnote: Font = .custom("Menlo", size: 13) public var caption: Font = .custom("Menlo", size: 12) public var caption2: Font = .custom("Menlo", size: 11) public var callout: Font = .custom("Menlo", size: 17) public var largeTitle: Font = .custom("Menlo", size: 34) public var title1: Font = .custom("Menlo", size: 28) public var title3: Font = .custom("Menlo", size: 20) public init() {} } ``` Setup this in a place convenient for you in your application: ```Swift Swift theme={null} var appTheme: AppTheme = AppTheme(color: QuickBloxUIKit.ThemeColor(), font: CustomThemeFont(), image: QuickBloxUIKit.ThemeImage(), string: QuickBloxUIKit.ThemeString()) QuickBloxUIKit.settings.theme = appTheme // Custom font theme setting. ``` <img /> <img /> ## Image Theme The default ImageTheme conforms to the ThemeImageProtocol: ```Swift Swift theme={null} public protocol ThemeImageProtocol { var avatarUser: Image { get set } var avatarGroup: Image { get set } var avatarPublic: Image { get set } var user: Image { get set } var groupChat: Image { get set } var publicChannel: Image { get set } var leave: Image { get set } var leavePNG: Image { get set } var newChat: Image { get set } var back: Image { get set } var close: Image { get set } var conference: Image { get set } var chat: Image { get set } var camera: Image { get set } var avatarCamera: Image { get set } var checkmark: Image { get set } var attachmentPlaceholder: Image { get set } var info: Image { get set } var bell: Image { get set } var magnifyingglass: Image { get set } var chevronForward: Image { get set } var trash: Image { get set } var plus: Image { get set } var mic: Image { get set } var smiley: Image { get set } var paperclip: Image { get set } var paperplane: Image { get set } var keyboard: Image { get set } var record: Image { get set } var wave: Image { get set } var play: Image { get set } var pause: Image { get set } var photo: Image { get set } var delivered: Image { get set } var read: Image { get set } var send: Image { get set } var doctext: Image { get set } var speakerwave: Image { get set } var robot: Image { get set } } ``` with such default images: ```Swift Swift theme={null} public class ThemeImage: ThemeImageProtocol { public var avatarUser: Image = Image("AvatarUser", bundle: .module) public var avatarGroup: Image = Image("AvatarGroup", bundle: .module) public var avatarPublic: Image = Image("AvatarPublic", bundle: .module) public var user: Image = Image(systemName: "person") public var groupChat: Image = Image(systemName: "person.2") public var publicChannel: Image = Image(systemName: "megaphone") public var leave: Image = Image(systemName: "rectangle.portrait.and.arrow.forward") public var leavePNG: Image = Image("Leave", bundle: .module) public var newChat: Image = Image(systemName: "square.and.pencil") public var back: Image = Image(systemName: "chevron.backward") public var close: Image = Image(systemName: "xmark") public var conference: Image = Image(systemName: "person.3") public var chat: Image = Image(systemName: "message") public var camera: Image = Image(systemName: "camera") public var avatarCamera: Image = Image("AvatarCamera", bundle: .module) public var checkmark: Image = Image(systemName: "checkmark") public var attachmentPlaceholder: Image = Image("attachmentPlaceholder", bundle: .module) public var info: Image = Image(systemName: "info.circle") public var bell: Image = Image(systemName: "bell") public var magnifyingglass: Image = Image(systemName: "magnifyingglass") public var chevronForward: Image = Image(systemName: "chevron.forward") public var trash: Image = Image(systemName: "trash") public var plus: Image = Image(systemName: "plus.app") public var mic: Image = Image(systemName: "mic") public var smiley: Image = Image(systemName: "smiley") public var paperclip: Image = Image(systemName: "paperclip") public var paperplane: Image = Image(systemName: "paperplane.fill") public var keyboard: Image = Image(systemName: "keyboard") public var record: Image = Image(systemName: "record.circle") public var wave: Image = Image("wave", bundle: .module) public var play: Image = Image(systemName: "play.fill") public var pause: Image = Image(systemName: "pause.fill") public var photo: Image = Image(systemName: "photo") public var delivered: Image = Image("delivered", bundle: .module) public var read: Image = Image("delivered", bundle: .module) public var send: Image = Image("send", bundle: .module) public var doctext: Image = Image(systemName: "doc.text.fill") public var speakerwave: Image = Image(systemName: "speaker.wave.1.fill") public var robot: Image = Image("Robot", bundle: .module) public init() {} } ``` ### Use your own Image Theme You can create and use your own images theme. To do this you need to create a class that implements `QuickBloxUIKit.ThemeImageProtocol` with the images you need. For example set up images for the Info Dialog screen: ```Swift Swift theme={null} public class CustomImageTheme: ThemeImageProtocol { public var avatarUser: Image = Image("AvatarUser") public var avatarGroup: Image = Image("AvatarGroup") public var avatarPublic: Image = Image("AvatarPublic") public var user: Image = Image(systemName: "person") public var groupChat: Image = Image(systemName: "person.3") // default: person.2 public var publicChannel: Image = Image(systemName: "megaphone") public var leave: Image = Image(systemName: "person.fill.xmark") // default: rectangle.portrait.and.arrow.forward public var leavePNG: Image = Image("Leave") public var newChat: Image = Image(systemName: "square.and.pencil") public var back: Image = Image(systemName: "chevron.backward") public var close: Image = Image(systemName: "xmark") public var conference: Image = Image(systemName: "person.3") public var chat: Image = Image(systemName: "message") public var camera: Image = Image(systemName: "camera") public var avatarCamera: Image = Image("AvatarCamera") public var checkmark: Image = Image(systemName: "checkmark") public var attachmentPlaceholder: Image = Image("attachmentPlaceholder") public var info: Image = Image(systemName: "info.circle") public var bell: Image = Image(systemName: "bell") public var magnifyingglass: Image = Image(systemName: "magnifyingglass.circle") // default: magnifyingglass public var chevronForward: Image = Image(systemName: "chevron.forward") public var trash: Image = Image(systemName: "trash") public var plus: Image = Image(systemName: "plus.app") public var mic: Image = Image(systemName: "mic") public var smiley: Image = Image(systemName: "smiley") public var paperclip: Image = Image(systemName: "paperclip") public var paperplane: Image = Image(systemName: "paperplane.fill") public var keyboard: Image = Image(systemName: "keyboard") public var record: Image = Image(systemName: "record.circle") public var wave: Image = Image("wave") public var play: Image = Image(systemName: "play.fill") public var pause: Image = Image(systemName: "pause.fill") public var photo: Image = Image(systemName: "photo") public var delivered: Image = Image("delivered") public var read: Image = Image("delivered") public var send: Image = Image("send") public var doctext: Image = Image(systemName: "doc.text.fill") public var speakerwave: Image = Image(systemName: "speaker.wave.1.fill") public var message: Image = Image(systemName: "message") public var robot: Image = Image("Robot") public init() {} } ``` Setup this in a place convenient for you in your application: ```Swift Swift theme={null} var appTheme: AppTheme = AppTheme(color: QuickBloxUIKit.ThemeColor(), font: CustomThemeFont(), image: CustomThemeImage(), string: QuickBloxUIKit.ThemeString()) QuickBloxUIKit.settings.theme = appTheme // Custom images and fonst themes setting. ``` <img /> <img /> ## String Theme The default StringTheme conforms to the ThemeStringProtocol: ```Swift Swift theme={null} public class ThemeString: ThemeStringProtocol { public var dialogsEmpty: String = String(localized: "dialog.items.empty", bundle: .module) public var usersEmpty: String = String(localized: "dialog.members.empty", bundle: .module) public var messegesEmpty: String = String(localized: "dialog.messages.empty", bundle: .module) public var privateDialog: String = String(localized: "dialog.type.private", bundle: .module) public var groupDialog: String = String(localized: "dialog.type.group", bundle: .module) public var publicDialog: String = String(localized: "dialog.type.group", bundle: .module) public var typingOne: String = String(localized: "dialog.typing.one", bundle: .module) public var typingTwo: String = String(localized: "dialog.typing.two", bundle: .module) public var typingFour: String = String(localized: "dialog.typing.four", bundle: .module) public var enterName: String = String(localized: "alert.actions.enterName", bundle: .module) public var nameHint: String = String(localized: "dialog.name.hint", bundle: .module) public var create: String = String(localized: "dialog.name.create", bundle: .module) public var next: String = String(localized: "dialog.name.next", bundle: .module) public var search: String = String(localized: "dialog.name.search", bundle: .module) public var edit: String = String(localized: "dialog.info.edit", bundle: .module) public var members: String = String(localized: "dialog.info.members", bundle: .module) public var notification: String = String(localized: "dialog.info.notification", bundle: .module) public var searchInDialog: String = String(localized: "dialog.info.searchInDialog", bundle: .module) public var leaveDialog: String = String(localized: "dialog.info.leaveDialog", bundle: .module) public var you: String = String(localized: "dialog.info.you", bundle: .module) public var admin: String = String(localized: "dialog.info.admin", bundle: .module) public var typeMessage: String = String(localized: "dialog.action.typeMessage", bundle: .module) public var dialogs: String = String(localized: "screen.title.dialogs", bundle: .module) public var dialog: String = String(localized: "screen.title.dialog", bundle: .module) public var dialogType: String = String(localized: "screen.title.dialogType", bundle: .module) public var newDialog: String = String(localized: "screen.title.newDialog", bundle: .module) public var createDialog: String = String(localized: "screen.title.createDialog", bundle: .module) public var addMembers: String = String(localized: "screen.title.addMembers", bundle: .module) public var dialogInformation: String = String(localized: "screen.title.dialogInformation", bundle: .module) public var add: String = String(localized: "alert.actions.add", bundle: .module) public var dialogName: String = String(localized: "alert.actions.dialogName", bundle: .module) public var changeImage: String = String(localized: "alert.actions.changeImage", bundle: .module) public var changeDialogName: String = String(localized: "alert.actions.changeDialogName", bundle: .module) public var photo: String = String(localized: "alert.actions.photo", bundle: .module) public var removePhoto: String = String(localized: "alert.actions.removePhoto", bundle: .module) public var camera: String = String(localized: "alert.actions.camera", bundle: .module) public var gallery: String = String(localized: "alert.actions.gallery", bundle: .module) public var file: String = String(localized: "alert.actions.file", bundle: .module) public var remove: String = String(localized: "alert.actions.remove", bundle: .module) public var cancel: String = String(localized: "alert.actions.cancel", bundle: .module) public var ok: String = String(localized: "alert.actions.ok", bundle: .module) public var removeUser: String = String(localized: "alert.message.removeUser", bundle: .module) public var questionMark: String = String(localized: "alert.message.questionMark", bundle: .module) public var errorValidation: String = String(localized: "alert.message.errorValidation", bundle: .module) public var addUser: String = String(localized: "alert.message.addUser", bundle: .module) public var toDialog: String = String(localized: "alert.message.toDialog", bundle: .module) public var maxSize: String = String(localized: "attachment.maxSize.title", bundle: .module) public var maxSizeHint: String = String(localized: "attachment.maxSize.hint", bundle: .module) public var fileTitle: String = String(localized: "attachment.title.file", bundle: .module) public var gif: String = String(localized: "attachment.title.gif", bundle: .module) public init() {} } ``` By default, QuickBlox iOS UIKit supports English localization: ```Swift Swift theme={null} // swift-tools-version: 5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "QuickBloxUIKit", defaultLocalization: "en", platforms: [ .iOS(.v15) ], ``` with such default strings: ```Swift Swift theme={null} `"dialog.items.empty" = "You don’t have any dialogs."; "dialog.members.empty" = "You don’t have any users."; "dialog.messages.empty" = "You don’t have any messages."; "dialog.type.private" = "Private"; "dialog.type.group" = "Group"; "dialog.type.public" = "Public"; "dialog.typing.one" = " is typing..."; "dialog.typing.two" = " are typing..."; "dialog.typing.four" = " and 2 others are typing..."; "dialog.name.hint" = "Use alphanumeric characters and spaces in a range from 3 to 60. Cannot contain more than one space in a row."; "dialog.name.create" = "Create"; "dialog.name.next" = "Next"; "dialog.name.search" = "Search"; "dialog.name.cancel" = "Cancel"; "dialog.info.edit" = "Edit"; "dialog.info.members" = "Members"; "dialog.info.notification" = "Notification"; "dialog.info.searchInDialog" = "Search in dialog"; "dialog.info.leaveDialog" = "Leave dialog"; "dialog.info.you" = " (You)"; "dialog.info.admin" = "Admin"; "dialog.action.typeMessage" = "Type message"; "screen.title.dialogs" = "Dialogs"; "screen.title.dialog" = "Dialog"; "screen.title.dialogType" = "Dialog type"; "screen.title.newDialog" = "New Dialog"; "screen.title.createDialog" = "Create Dialog"; "screen.title.addMembers" = "Add Members"; "screen.title.dialogInformation" = "Dialog information"; "alert.actions.add" = "Add"; "alert.actions.dialogName" = "Dialog name"; "alert.actions.enterName" = "Enter name"; "alert.actions.changeImage" = "Change image"; "alert.actions.changeDialogName" = "Change dialog name"; "alert.actions.photo" = "Photo"; "alert.actions.removePhoto" = "Remove photo"; "alert.actions.camera" = "Camera"; "alert.actions.gallery" = "Gallery"; "alert.actions.file" = "File"; "alert.actions.remove" = "Remove"; "alert.actions.cancel" = "Cancel"; "alert.actions.ok" = "Ok"; "alert.message.removeUser" = "Are you sure you want to remove "; "alert.message.questionMark" = "?"; "alert.message.errorValidation" = "Error Validation"; "alert.message.addUser" = "Are you sure you want to add "; "alert.message.toDialog" = " to this dialog?"; "attachment.maxSize.title" = "The uploaded file exceeds maximum file size (10MB)"; "attachment.maxSize.hint" = "Please select a smaller attachment."; "attachment.title.file" = "file"; "attachment.title.gif" = "GIF"; ``` ### Use your own String Theme You can use your string values to uniquely design your application: ```Swift Swift theme={null} `"dialog.items.empty" = "You don’t have any channels."; "dialog.members.empty" = "You don’t have any users."; "dialog.messages.empty" = "You don’t have any messages."; "dialog.type.private" = "Private"; "dialog.type.group" = "Group"; "dialog.type.public" = "Public"; "dialog.typing.one" = " is typing..."; "dialog.typing.two" = " are typing..."; "dialog.typing.four" = " and 2 others are typing..."; "dialog.name.hint" = "Use alphanumeric characters and spaces in a range from 3 to 60. Cannot contain more than one space in a row."; "dialog.name.create" = "Create"; "dialog.name.next" = "Next"; "dialog.name.search" = "Search"; "dialog.name.cancel" = "Cancel"; "dialog.info.edit" = "Edit"; "dialog.info.members" = "Members"; "dialog.info.notification" = "Notification"; "dialog.info.searchInDialog" = "Search in channel"; "dialog.info.leaveDialog" = "Leave channel"; "dialog.info.you" = " (You)"; "dialog.info.admin" = "Admin"; "dialog.action.typeMessage" = "Type message"; "screen.title.dialogs" = "Channels"; "screen.title.dialog" = "Channel"; "screen.title.dialogType" = "Channel type"; "screen.title.newDialog" = "New Channel"; "screen.title.createDialog" = "Create Channel"; "screen.title.addMembers" = "Add Members"; "screen.title.dialogInformation" = "Channel information"; "alert.actions.add" = "Add"; "alert.actions.dialogName" = "Channel name"; "alert.actions.enterName" = "Enter name"; "alert.actions.changeImage" = "Change image"; "alert.actions.changeDialogName" = "Change channel name"; "alert.actions.photo" = "Photo"; "alert.actions.removePhoto" = "Remove photo"; "alert.actions.camera" = "Camera"; "alert.actions.gallery" = "Gallery"; "alert.actions.file" = "File"; "alert.actions.remove" = "Remove"; "alert.actions.cancel" = "Cancel"; "alert.actions.ok" = "Ok"; "alert.message.removeUser" = "Are you sure you want to remove "; "alert.message.questionMark" = "?"; "alert.message.errorValidation" = "Error Validation"; "alert.message.addUser" = "Are you sure you want to add "; "alert.message.toDialog" = " to this channel?"; "attachment.maxSize.title" = "The uploaded file exceeds maximum file size (10MB)"; "attachment.maxSize.hint" = "Please select a smaller attachment."; "attachment.title.file" = "file"; "attachment.title.gif" = "GIF"; ``` You can create and use your own string theme. To do this you need to create class that implements `QuickBloxUIKit.ThemeStringProtocol`: ```Swift Swift theme={null} public class CustomThemeString: ThemeStringProtocol { public var dialogsEmpty: String = String(localized: "dialog.items.empty") public var usersEmpty: String = String(localized: "dialog.members.empty") public var messegesEmpty: String = String(localized: "dialog.messages.empty") public var privateDialog: String = String(localized: "dialog.type.private") public var groupDialog: String = String(localized: "dialog.type.group") public var publicDialog: String = String(localized: "dialog.type.group") public var typingOne: String = String(localized: "dialog.typing.one") public var typingTwo: String = String(localized: "dialog.typing.two") public var typingFour: String = String(localized: "dialog.typing.four") public var enterName: String = String(localized: "alert.actions.enterName") public var nameHint: String = String(localized: "dialog.name.hint") public var create: String = String(localized: "dialog.name.create") public var next: String = String(localized: "dialog.name.next") public var search: String = String(localized: "dialog.name.search") public var edit: String = String(localized: "dialog.info.edit") public var members: String = String(localized: "dialog.info.members") public var notification: String = String(localized: "dialog.info.notification") public var searchInDialog: String = String(localized: "dialog.info.searchInDialog") public var leaveDialog: String = String(localized: "dialog.info.leaveDialog") public var you: String = String(localized: "dialog.info.you") public var admin: String = String(localized: "dialog.info.admin") public var typeMessage: String = String(localized: "dialog.action.typeMessage") public var dialogs: String = String(localized: "screen.title.dialogs") public var dialog: String = String(localized: "screen.title.dialog") public var dialogType: String = String(localized: "screen.title.dialogType") public var newDialog: String = String(localized: "screen.title.newDialog") public var createDialog: String = String(localized: "screen.title.createDialog") public var addMembers: String = String(localized: "screen.title.addMembers") public var dialogInformation: String = String(localized: "screen.title.dialogInformation") public var add: String = String(localized: "alert.actions.add") public var dialogName: String = String(localized: "alert.actions.dialogName") public var changeImage: String = String(localized: "alert.actions.changeImage") public var changeDialogName: String = String(localized: "alert.actions.changeDialogName") public var photo: String = String(localized: "alert.actions.photo") public var removePhoto: String = String(localized: "alert.actions.removePhoto") public var camera: String = String(localized: "alert.actions.camera") public var gallery: String = String(localized: "alert.actions.gallery") public var file: String = String(localized: "alert.actions.file") public var remove: String = String(localized: "alert.actions.remove") public var cancel: String = String(localized: "alert.actions.cancel") public var ok: String = String(localized: "alert.actions.ok") public var removeUser: String = String(localized: "alert.message.removeUser") public var questionMark: String = String(localized: "alert.message.questionMark") public var errorValidation: String = String(localized: "alert.message.errorValidation") public var addUser: String = String(localized: "alert.message.addUser") public var toDialog: String = String(localized: "alert.message.toDialog") public var maxSize: String = String(localized: "attachment.maxSize.title") public var maxSizeHint: String = String(localized: "attachment.maxSize.hint") public var fileTitle: String = String(localized: "attachment.title.file") public var gif: String = String(localized: "attachment.title.gif") public init() {} } ``` Setup this in a place convenient for you in your application: ```Swift Swift theme={null} var appTheme: AppTheme = AppTheme(color: CustomThemeColor(), font: QuickBloxUIKit.ThemeFont(), image: QuickBloxUIKit.ThemeImage(), string: CustomThemeString()) QuickBloxUIKit.settings.theme = appTheme // Custom color and string themes setting. ``` Your string values will apply to the QuickBlox iOS UIKit user interface, for example: <Frame> <img alt="UIKit Example" /> </Frame> **Use Localization to expand the capabilities of your application.** For custom localization, customize your application by adding the necessary localization files. You can learn more about how to do this at the [Apple Documentation](https://developer.apple.com/documentation/xcode/localization). 1. Copy and add to the localization file in your project the necessary string constants from QuickBlox iOS UIKit. If you do not have a localization file yet, then create it following the guide from [Apple Documentation](https://developer.apple.com/documentation/xcode/localization). 2. [Customize](/ui-kits/ios-uikit-customization#use-your-own-string-theme) these constants as you need for your purposes. 3. Create and add the necessary localization files to your project. For example, add a localization file for Spanish to your project: <Frame> <img alt="Localization Example" /> </Frame> 1. Customize localization files according to the language they represent. For example, add a localization file for Spanish: ```Swift Swift theme={null} "dialog.items.empty" = "No tiene ningún cuadro de canal."; "dialog.members.empty" = "No tiene ningún usuario."; "dialog.messages.empty" = "No tiene ningún mensaje."; "dialog.type.private" = "Privado"; "dialog.type.group" = "Grupo"; "dialog.type.public" = "Público"; "dialog.typing.one" = " está escribiendo..."; "dialog.typing.two" = " están escribiendo..."; "dialog.typing.four" = " y otros 2 están escribiendo..."; "dialog.name.hint" = "Use caracteres alfanuméricos y espacios en un rango de 3 a 60. No puede contener más de un espacio en una fila."; "dialog.name.create" = "Crear"; "dialog.name.next" = "Siguiente"; "dialog.name.search" = "Buscar"; "dialog.name.cancel" = "Cancelar"; "dialog.info.edit" = "Editar"; "dialog.info.members" = "Miembros"; "dialog.info.notification" = "Notificación"; "dialog.info.searchInDialog" = "Buscar en canal"; "dialog.info.leaveDialog" = "Salir del cuadro de canal"; "dialog.info.you" = " (usted)"; "dialog.info.admin" = "Admin"; "dialog.action.typeMessage" = "Escribir mensaje"; "screen.title.dialogs" = "Canales"; "screen.title.dialog" = "Canal"; "screen.title.dialogType" = "Tipo de canal"; "screen.title.newDialog" = "Canal nuevo"; "screen.title.createDialog" = "Crear canal"; "screen.title.addMembers" = "Agregar miembros"; "screen.title.dialogInformation" = "Información de canal"; "alert.actions.add" = "Agregar"; "alert.actions.dialogName" = "Nombre del canal"; "alert.actions.enterName" = "Ingrese el nombre"; "alert.actions.changeImage" = "Cambiar imagen"; "alert.actions.changeDialogName" = "Cambiar nombre de canal"; "alert.actions.photo" = "Foto"; "alert.actions.removePhoto" = "Eliminar foto"; "alert.actions.camera" = "Cámara"; "alert.actions.gallery" = "Galería"; "alert.actions.file" = "Archivo"; "alert.actions.remove" = "Eliminar"; "alert.actions.cancel" = "Cancelar"; "alert.actions.ok" = "Ok"; "alert.message.removeUser" = "¿Está seguro de que desea eliminar "; "alert.message.questionMark" = "?"; "alert.message.errorValidation" = "Error de validación"; "alert.message.addUser" = "¿Está seguro de que desea agregar "; "alert.message.toDialog" = "¿a este cuadro de canal?"; "attachment.maxSize.title" = "El archivo cargado supera el tamaño máximo de archivo (10 MB)"; "attachment.maxSize.hint" = "Seleccione un archivo adjunto más pequeño."; "attachment.title.file" = "archivo"; "attachment.title.gif" = "GIF"; ``` Now if Spanish is set as the main language on the device of the user of your application, then your application will automatically respond to this and the user interface will change: <Frame> <img alt="UIKit Example" /> </Frame> # Features Source: https://docs.quickblox.com/ui-kits/ios-uikit-features QuickBlox UIKit for iOS provides an extensive range of features to enhance the chat experience within applications. ## Overview By incorporating essential messaging functionalities such as typing indicators, delivered/read receipts, and other functionality, users can engage in more interactive and meaningful conversations. The table below presents a comprehensive list of features supported by QuickBlox UIKit, categorized by dialogs type, along with indications of the specific dialogs types that support each feature. <div> <table> <thead> <tr> <th>Feature</th> <th>Group dialog</th> <th>Private dialog</th> </tr> </thead> <tbody> <tr> <td>`File sharing`</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>`Voice message`</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>`Typing indicator`</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>`Delivery receipt`</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>`Read receipt`</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>`Unread message count`</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>`Highlight URLs`</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>`Forward message`</td> <td /> <td /> </tr> <tr> <td>`Reply message`</td> <td /> <td /> </tr> </tbody> </table> </div> ### Dialog types QuickBlox UIKit provides support for the following types: #### Group dialog A group dialog is a chat between closed group of people. New participant can be added either by dialog owner or any other participant of the group dialog. #### Private dialog A private dialog is a direct chat between two people, it can't be extended to host more people. ## File sharing File sharing is a fundamental chat feature that allows users to share different types of files such as images, audio files, videos, and other file types during a conversation. The ability to share files is conveniently available through the messageInput component. When using the file sharing feature, users can send four different types of file messages: image files, video files, audio files, and document files. These files can be easily shared with other participants in the conversation. Users can take a photo with the mobile device's camera directly, or upload an existing image, video, audio, or document file from their mobile device to share it with others in a chat. <Frame> <img /> </Frame> Note: Prior to utilizing the file sharing feature, it is essential to create a dialog. To gain insights into enabling users to engage in conversations, please refer to the "Messaging in a Dialog" section for detailed instructions. This section will provide you with comprehensive guidance on facilitating chat functionality in your application's. ### Customizing the UI for File Sharing To provide a personalized user experience for file sharing, you can customize the UI elements using various components available in the QuickBlox UIKit. The following sections outline the customization options and properties that can be adjusted: You can customize the UI for file sharing using string resource and icon resource. #### String resource The string resource is a set of strings used to compose the screens described in `ThemeString` containing QuickBlox UIKit-defined string values. ```Swift swift theme={null} public class ThemeString: ThemeStringProtocol { ... public var photo: String = String(localized: "alert.actions.photo", bundle: .module) public var removePhoto: String = String(localized: "alert.actions.removePhoto", bundle: .module) public var camera: String = String(localized: "alert.actions.camera", bundle: .module) public var gallery: String = String(localized: "alert.actions.gallery", bundle: .module) public var file: String = String(localized: "alert.actions.file", bundle: .module) ... public var maxSize: String = String(localized: "attachment.maxSize.title", bundle: .module) public var maxSizeHint: String = String(localized: "attachment.maxSize.hint", bundle: .module) public var fileTitle: String = String(localized: "attachment.title.file", bundle: .module) public var gif: String = String(localized: "attachment.title.gif", bundle: .module) public init() {} } ``` You can use your string values to uniquely design your application: ```Swift Swift theme={null} QuickBloxUIKit.settings.theme.string.gallery = "Photo and video" // "Gallery" by default QuickBloxUIKit.settings.theme.string.camera = "Take photo from camera" // "Camera" by default ``` Use Localization to expand the capabilities of your application. This feature is described in the [Customization](/ui-kits/ios-uikit-customization#use-your-own-string-theme) section. #### A set of icons for file sharing The file sharing icon set is part of the QuickBlox UIKit icons described in `ThemeImage`. It allows you to customize the icons used throughout the user interface, including those related to file sharing. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>paperclip</td> <td> <img /> </td> <td>Add a file. <br /> The system symbol image.</td> </tr> <tr> <td>play</td> <td> <img /> </td> <td>Play a video/audio file. <br /> The system symbol image.</td> </tr> <tr> <td>pause</td> <td> <img /> </td> <td>Pause/stop a video/audio file. <br /> The system symbol image.</td> </tr> <tr> <td>gif</td> <td> <img /> </td> <td>GIF, Graphics Interchange Format, file. <br /> Text used.</td> </tr> <tr> <td>photo</td> <td> <img /> </td> <td>Image file. <br /> The system symbol image.</td> </tr> <tr> <td>doctext</td> <td> <img /> </td> <td>Document file message. <br /> The system symbol image.</td> </tr> </tbody> </table> Using these customization options, you can tailor the file sharing user interface to match your app's branding and design guidelines, improving the overall user experience. ```Swift Swift theme={null} QuickBloxUIKit.settings.theme.image.paperclip = Image("Your_paperclip_image") QuickBloxUIKit.settings.theme.image.doctext = Image("Your_doctext_image") ``` For full details on how to use your images to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-image-theme). ## Voice message Voice messaging is a valuable feature that provides users with an alternative means of communication, enabling them to exchange messages without relying on text or file-based content. With voice messages, users can convey their thoughts, emotions, and intentions more effectively by utilizing the power of their own voice. By utilizing voice messages, users can benefit from increased efficiency and convenience. It takes significantly less time to record a voice message compared to composing a lengthy text message, allowing the sender to save time and effort. Furthermore, the receiver can listen to the voice message at their convenience, even while multitasking or engaged in other activities. To send a voice message, users can utilize the input component provided by QuickBlox UIKit. By recording and sending the voice message using this component, it will be displayed in the message list component of the dialog. The receiver can then easily access the voice message and play it to listen to its contents. <Frame> <img /> </Frame> ### Sending a voice message: To send a voice message, users need to record their message using the inputView available in the `DialogView`. By default, a voice message icon is displayed on the right side of the input field, indicating that the voice recording feature is available. Clicking this icon will initiate the burning process and the icon will turn red. The user can stop recording by clicking on the same button. To cancel sending a voice message and return to the text input field, the user can click on the cancel button on the left. To send a recorded voice message, users can click the send button on the right. When the recording process completes, the recorder will deactivate the audio session configured by QuickBlox UIKit and attempt to reactivate the client application's audio session. For instructions on setting up an audio session in QuickBlox UIKit and playing audio and video files, see the corresponding guide in the documentation. #### Receiving voice messages: Voice messages can be received in all types of dialogues, just like any other messages exchanged between participants in a dialogue. Received and sent voice messages are displayed as two components: `InboundAudioMessageRow` and `OutboundAudioMessageRow`. When a voice message is sent and received it will allow users to access and view received voice messages. It is important to note that in dialog, users can listen to only one voice message at a time. If an attempt is made to play another voice message while listening to the previous one, the previous message being played will be paused to accommodate the new playback. #### To end voice message playback: Upon completion of listening to a voice message, the player responsible for processing playback deactivates the audio session configured using QuickBlox UIKit. Subsequently, it will attempt to reactivate the audio session associated with the client application. For detailed information about setting up and activating an audio session, see the Apple Developer Guide, which provides a comprehensive guide to working with audio sessions. ### Customizing the UI for Voice message To provide a personalized user experience for voice messages, you can customize the UI elements using various components available in the QuickBlox UIKit. The following sections outline the customization options and properties that can be adjusted: #### A set of icons for Voice message The voice message icon set is part of the QuickBlox UIKit icons described in `ThemeImage`. It allows you to customize the icons used throughout the user interface, including those related to voice messages. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>mic</td> <td> <img /> </td> <td>Start/stop voice message recording. <br /> The system symbol image.</td> </tr> <tr> <td>record</td> <td> <img /> </td> <td>Record a voice message. <br /> The system symbol image.</td> </tr> <tr> <td>close</td> <td> <img /> </td> <td>Cancel sending a voice message. <br /> The system symbol image.</td> </tr> <tr> <td>play</td> <td> <img /> </td> <td>Play a voice message. <br /> The system symbol image.</td> </tr> <tr> <td>pause</td> <td> <img /> </td> <td>Pause/stop a voice message. <br /> The system symbol image.</td> </tr> </tbody> </table> Using these customization options, you can tailor the voice message user interface to match your app's branding and design guidelines, improving the overall user experience. ```Swift Swift theme={null} QuickBloxUIKit.settings.theme.image.mic = Image("Your_mic_image") QuickBloxUIKit.settings.theme.image.record = Image("Your_record_image") ``` For full details on how to use your images to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-image-theme). ## Typing indicator The typing indicator is a valuable feature that provides users with visual cues that indicate when another user is actively composing a message in a chat. This feature enhances real-time communication by providing visibility into ongoing typing activities. The input indicator UI is displayed in the `DialogView` component between `MessagesScrollView` and `InputView`. This ensures that users can easily observe and determine when other participants are typing. The input indicator remains visible until the user submits a message or deletes all text. Also, if the user pauses while typing for more than 5 seconds, the typing indicator will automatically disappear, providing an accurate view of the current typing state of the dialog. <Frame> <img /> </Frame> ### Customizing the UI for Typing indicator: To provide a personalized user experience for typing indicators, you can customize the UI elements using various components available in the QuickBlox UIKit. The following sections outline the customization options and properties that can be adjusted: You can use your values to uniquely design your application: ```Swift Swift theme={null} QuickBloxUIKit.settings.theme.string.typingOne = " is typing..." QuickBloxUIKit.settings.theme.string.typingTwo = " are typing..." QuickBloxUIKit.settings.theme.string.typingFour = " and others are typing..." QuickBloxUIKit.settings.dialogScreen.typing.color = .black QuickBloxUIKit.settings.dialogScreen.typing.font = .caption2 ``` For full details on how to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-color-theme). ## Delivery receipt The delivery receipt is a feature that informs a user if their messages have been successfully delivered to other users in the dialog. When message reaches server, it is marked as sent, a single-tick icon appears next to the message’s timestamp. <img alt="" /> Once message reaches receiver device, it is marked as delivered. If the sender's message has been delivered to any recipient in the dialog, a double-tick icon appears next to the message's timestamp. The delivery receipt can only be seen by the sender of the message. <img alt="" /> ### Customizing the UI for Delivered receipt To provide a personalized user experience for delivered receipt, you can customize the UI elements using various components available in the QuickBlox UIKit. The following sections outline the customization options and properties that can be adjusted: #### A set of icons for Delivered receipt The delivered receipt icon set is part of the QuickBlox UIKit icons described in `ThemeImage`. It allows you to customize the icons used throughout the user interface, including those related to delivered receipt. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>send</td> <td> <img /> </td> <td>Used to indicate that the message was successfully sent.</td> </tr> <tr> <td>delivered</td> <td> <img /> </td> <td>Used to indicate that a message was successfully delivered.</td> </tr> </tbody> </table> Using these customization options, you can tailor the delivered receipt user interface to match your app's branding and design guidelines, improving the overall user experience. ```Swift Swift theme={null} QuickBloxUIKit.settings.theme.image.send = Image("Your_send_image") QuickBloxUIKit.settings.theme.image.delivered = Image("Your_delivered_image") ``` For full details on how to use your images to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-image-theme). Customize the icon color according to your app design: ```Swift Swift theme={null} QuickBloxUIKit.settings.dialogScreen.messageRow.sendForeground = .gray QuickBloxUIKit.settings.dialogScreen.messageRow.deliveredForeground = .gray ``` For full details on how to use your Color Theme to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-color-theme). ## Read receipt The read receipt feature allows users to determine whether their messages have been read by others in the dialog. When any recipient in the dialog have read the sender's message, a colored double-tick icon is displayed next to the message's timestamp. The read receipts are only visible to the sender of the message. <img alt="" /> ### Customizing the UI for Read receipt To provide a personalized user experience for read receipt, you can customize the UI elements using various components available in the QuickBlox UIKit. The following sections outline the customization options and properties that can be adjusted: #### A set of icons for Read receipt The read receipt icon set is part of the QuickBlox UIKit icons described in `ThemeImage`. It allows you to customize the icons used throughout the user interface, including those related to read receipt. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>read</td> <td> <img /> </td> <td>Used to indicate that the message was successfully read. Color modified</td> </tr> </tbody> </table> Using these customization options, you can tailor the read receipt user interface to match your app's branding and design guidelines, improving the overall user experience. ```Swift Swift theme={null} QuickBloxUIKit.settings.theme.image.read = Image("Your_read_image") ``` For full details on how to use your images to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-image-theme). Customize the icon color according to your app design: ```Swift Swift theme={null} QuickBloxUIKit.settings.dialogScreen.messageRow.readForeground = .green ``` For full details on how to use your Color Theme to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-color-theme). ## Unread message count The unread message count feature is designed to provide users with important information about the number of unread messages in each conversation they are in. This feature allows users to be informed about the volume of messages they have yet to read, ensuring they never miss important conversations. The unread message count is displayed in the `DialogRowView` which is the `DialogsListView` list component of the `DialogsView` structure, making it easily accessible and visible to users. A counter is specifically shown below the timestamp of the last message received for each conversation in the list. To ensure a concise and convenient display, if the number of unread messages exceeds `100`, it will be presented as `99+`, indicating a large number of unread messages. <div> <img alt="image" /> </div> This feature allows users to quickly identify dialogs with a high number of unread messages, allowing them to prioritize their interaction and stay up to date with the latest conversations. ### Customizing the UI for Unread message count To provide a personalized user experience for counting unread messages, you can customize the user interface elements using the various components available in QuickBlox UIKit. The following example describes the configuration options and properties that you can configure: ```Swift Swift theme={null} QuickBloxUIKit.settings.dialogsScreen.dialogRow.unreadCount.maxCount = 1000 QuickBloxUIKit.settings.dialogsScreen.dialogRow.unreadCount.backgroundColor = .green QuickBloxUIKit.settings.dialogsScreen.dialogRow.unreadCount.foregroundColor = .white QuickBloxUIKit.settings.dialogsScreen.dialogRow.unreadCount.font = .caption2 ``` For full details on how to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-color-theme). ## Highlight URLs When communicating via instant messengers, it is often useful to Highlight URLs to make them more visible and distinguish them from plain text. QuickBlox UIKit recognizes URLs and automatically converts them into clickable links. This allows recipients to easily access the associated web page. For example, if you simply include Highlight URLs like [www.example.com](http://www.example.com) in your message, the messenger will automatically convert it into a clickable link. <div> <img alt="image" /> </div> ### Customizing the UI for Highlight URLs QuickBlox UIKit supports text formatting options such as bold, italic, or underline. You can apply these formatting styles to Highlight URLs to make them stand out. For example: ```Swift Swift theme={null} QuickBloxUIKit.settings.dialogScreen.messageRow.linkFont = .callout.italic() QuickBloxUIKit.settings.dialogScreen.messageRow.linkUnderline = true // default QuickBloxUIKit.settings.dialogScreen.messageRow.inboundLinkForeground = .blue QuickBloxUIKit.settings.dialogScreen.messageRow.outboundLinkForeground = .blue ``` <div> <img alt="image" /> </div> This visually distinguishes them from the surrounding text and indicates that they are URLs. By using this, you can effectively Highlight URLs, making them more visible and easily recognizable to recipients. This ensures that your shared links get noticed and can be easily accessed. ## Forward message The Forward message feature in QuickBlox iOS UIKit enhances communication flexibility by allowing users to easily share messages with others in a chat. This feature is invaluable for circulating information, fostering collaboration, and maintaining a coherent conversation flow. Below is an overview of the forward message feature along with associated functionalities. Forwarding Messages: 1. Initiating Forward: Users can select a message within the chat that they wish to forward to another chat. Both the sender's messages and those from other participants are eligible for forwarding. <img alt="" /> Users can select and forward various multimedia content, including images, voice messages, and supported file types. <img alt="" /> 2\. Selecting Target Chat and Adding Comments or Annotations: Users are presented with a list of their existing chats to choose the destination for forwarding. Upon selecting the target chat, a confirmation step is required. Users have the option to add comments or annotations when forwarding any message type (text, image, file, etc.). Comment/Input Field: The comment/input field allows text entry. Added comments are included with the forwarded message, visible as a distinct message in the destination chat. Recipients can distinguish between the forwarded message content and added comments. <img alt="" /> The forwarded message promptly appears in the selected chat. <img alt="" /> 3\. Interaction and Notification: Recipients can interact with the forwarded message just like any other originally sent message. Users receive a confirmation notification upon successful forwarding. In case of failure, users are accurately notified with an appropriate error message. 4. AI Features: Recipients can apply AI Translation and AI Answer Assist features to forwarded text messages. Cancellation: If the user cancels the forwarding action, the message with added comments is not sent. ### Customizing the UI for Forward message To provide a personalized user experience for Forward messages, you can customize the UI elements using various components available in the QuickBlox UIKit. The following sections outline the customization options and properties that can be adjusted: #### A set of icons for Forward message The Forward message icon set is part of the QuickBlox UIKit icons described in `ThemeImage`. It allows you to customize the icons used throughout the user interface, including those related to Forward messages. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>forward</td> <td> <img /> </td> <td>Forward icon.</td> </tr> <tr> <td>forward filled</td> <td> <img /> </td> <td>Forward Filled icon.</td> </tr> </tbody> </table> Using these customization options, you can tailor the Forward message user interface to match your app's branding and design guidelines, improving the overall user experience. ```Swift Swift theme={null} QuickBloxUIKit.settings.theme.image.forwardIcon = Image("Your_forwardIcon_image") QuickBloxUIKit.settings.theme.image.forwardFilled = Image("Your_forwardFilled_image") ``` For full details on how to use your images to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-image-theme). ### Enable Forward message Feature Message forwarding is enabled by default. You can also turn it on and off: ```Swift Swift theme={null} QuickBloxUIKit.feature.forward.enable = true ``` ## Reply message The Reply message feature in QuickBlox iOS UIKit enhances communication flexibility by allowing users to easily Interaction with messages in a chat. This feature is invaluable for circulating information, fostering collaboration, and maintaining a coherent conversation flow. Below is an overview of the reply message feature along with associated functionalities. Reply Messages: 1. Initiating Reply: Users can select a message within the chat that they wish to reply in the chat. Both the sender's messages and those from other participants are eligible for reply. <img alt="" /> Users can select and reply various multimedia content, including images, voice messages, and supported file types. <img alt="" /> 2\. Adding Comments or Annotations: Users have the option to add comments or annotations when reply any message type (text, image, file, etc.). Comment/Input Field: The comment/input field allows text entry. Added comments are included with the replied message, visible as a distinct message in the chat. Recipients can distinguish between the replied message content and added comments. <img alt="" /> The replied message promptly appears in the chat. <img alt="" /> 3\. Interaction and Notification: Recipients can interact with the replied message just like any other originally sent message. Users receive a confirmation notification upon successful forwarding. In case of failure, users are accurately notified with an appropriate error message. 4. AI Features: Recipients can apply AI Translation and AI Answer Assist features to replied text messages. Cancellation: If the user cancels the replied action, the message with added comments is not sent. ### Customizing the UI for Reply message To provide a personalized user experience for Reply messages, you can customize the UI elements using various components available in the QuickBlox UIKit. The following sections outline the customization options and properties that can be adjusted: #### A set of icons for Reply message The Reply message icon set is part of the QuickBlox UIKit icons described in `ThemeImage`. It allows you to customize the icons used throughout the user interface, including those related to Reply messages. <table> <thead> <tr> <th>Icon name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>reply</td> <td> <img /> </td> <td>Reply icon.</td> </tr> <tr> <td>reply filled</td> <td> <img /> </td> <td>Reply Filled icon.</td> </tr> </tbody> </table> Using these customization options, you can tailor the Reply message user interface to match your app's branding and design guidelines, improving the overall user experience. ```Swift Swift theme={null} QuickBloxUIKit.settings.theme.image.reply = Image("Your_reply_image") QuickBloxUIKit.settings.theme.image.replyFilled = Image("Your_replyFilled_image") ``` For full details on how to use your images to customize your app, see [Customization](/ui-kits/ios-uikit-customization#use-your-own-image-theme). ### Enable Reply message Feature Message Reply is enabled by default. You can also turn it on and off: ```Swift Swift theme={null} QuickBloxUIKit.feature.reply.enable = true ``` # Overview Source: https://docs.quickblox.com/ui-kits/ios-uikit-overview The QuickBlox UIKit for iOS is a comprehensive user interface kit specifically designed for building chat applications. It provides a collection of pre-built components, modules, and utilities that simplify the process of creating chat applications. The main goal of the QuickBlox UIKit for iOS is to offer developers a streamlined and efficient way to implement chat functionality within their iOS applications. The QuickBlox UIKit for iOS offers modules that encapsulate complex chat functionalities, such as dialogs and chat management and real-time updates. These modules provide a simplified interface for integrating chat features into applications without the need for extensive knowledge of the underlying protocols or server-side infrastructure. <Frame> <img /> </Frame> <Frame> <img /> </Frame> ## Features * QuickBlox UIKit for iOS provides next functionality: * List of dialogs * Create dialog(Private or Group) * Dialog screen * Send text, image, video, audio, file messages * Dialog info screen * List, invite, remove members ## Source code You can find QuickBlox UIKit for iOS source code [here](https://github.com/QuickBlox/ios-ui-kit.git) <Note> Quick start using our [UIKitSample](https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-ui-kit). For a quick start using the features of the QuickBlox iOS UIKit, we recommend checking out our [UIKitSample](https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-ui-kit). This sample implements authorization functionality and provides an example of color theme customization. This sample already has a [QuickBlox iOS UIKit](https://github.com/QuickBlox/ios-ui-kit) connected as a Swift Package. How to set up and run a sample, see [Quick Start with a UIKitSample](/ui-kits/ios-uikit-send-your-first-message#sample) </Note> # Screens Source: https://docs.quickblox.com/ui-kits/ios-uikit-screens QuickBlox iOS UIKit provides customizable screens and basic chat features such as dialog list, dialog creation, chat in dialogs, and dialog editing out of the box. ## Overview Screens are made up of components, a `ScreenSettings` for that screen, and a `ViewModel` that provides the screen's functionality. Each screen has custom user interface components. Each screen has a specific `ScreenSettings` that creates the view and each `ScreenSettings` contains customizable UI components. A screen also has a corresponding `ViewModel` that provides the necessary data from **QuickBlox iOS SDK**. Refer to the table below to see which screens we provide and the components that make up each screen. <table> <thead> <tr> <th>Screen</th> <th>ScreenView</th> <th>ScreenSettings</th> <th>ViewModel</th> <th>Components</th> </tr> </thead> <tbody> <tr> <td><a href="/ui-kits/ios-uikit-screens#dialog-list-screen">Dialog list</a></td> <td>DialogsView</td> <td>DialogsScreenSettings</td> <td>DialogsViewModel</td> <td>DialogListHeader</td> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#private-dialog-screen">Private Dialog</a></td> <td>PrivateDialogView</td> <td>DialogScreenSettings</td> <td>DialogViewModel</td> <td>DialogHeader</td> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#private-dialog-info-screen">Private Dialog info</a></td> <td>PrivateDialogInfoView</td> <td>DialogInfoScreenSettings</td> <td>DialogInfoViewModel</td> <td>PrivateDialogInfoHeader</td> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#create-private-dialog-screen">Create Private Dialog</a></td> <td>CreateDialogView</td> <td>CreateDialogScreenSettings</td> <td>CreateDialogViewModel</td> <td>CreateDialogHeader</td> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#dialog-type-screen">Dialog Type</a></td> <td>DialogTypeView</td> <td>DialogTypeScreenSettings</td> <td>DialogTypeHeaderView</td> <td /> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#group-dialog-screen">Group Dialog</a></td> <td>GroupDialogView</td> <td>DialogScreenSettings</td> <td>DialogViewModel</td> <td>DialogHeader</td> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#group-dialog-info-screen">Group Dialog info</a></td> <td>GroupDialogInfoView</td> <td>DialogInfoScreenSettings</td> <td>DialogInfoViewModel</td> <td>DialogInfoHeader</td> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#members-remove-users-screen">Members</a></td> <td>RemoveMembersView</td> <td>MembersScreenSettings</td> <td>MembersDialogViewModel</td> <td>MembersHeader</td> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#add-members-screen">Add members</a></td> <td>AddMembersDialogView</td> <td>AddMembersScreenSettings</td> <td>AddMembersDialogViewModel</td> <td>AddMembersHeader</td> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#new-dialog-screen">New Dialog</a></td> <td>NewDialog</td> <td>DialogNameScreenSettings</td> <td>NewDialogViewModel</td> <td>DialogNameHeader</td> </tr> <tr> <td><a href="/ui-kits/ios-uikit-screens#create-group-dialog-screen">Create Group Dialog</a></td> <td>CreateDialogView</td> <td>CreateDialogScreenSettings</td> <td>CreateDialogViewModel</td> <td>CreateDialogHeader</td> </tr> </tbody> </table> ## Dialog list screen ### Overview The Dialog List feature in QuickBlox iOS UIKit provides a user interface for displaying and managing a list of dialogs in an iOS application. It allows users to view, select, and perform actions on individual dialogs, such as deleting or leaving a dialog. ### Features * Display a list of dialogs: The `DialogsListView` component presents a visually appealing list of dialogs in the application. <Frame> <img alt="UIKit Screenshot" /> </Frame> * Select and view dialog details: Users can select a dialog from the list to view more details about the selected dialog. <Frame> <img alt="View Dialog Details" /> </Frame> * Delete dialogs: The feature allows users to delete dialogs from the list, removing them from their conversation history. <Frame> <img alt="Delete Dialogs" /> </Frame> * Search functionality: Users can search for specific dialogs using the provided search bar. <Frame> <img alt="Search Functionality" /> </Frame> * Sync state indication: The sync state is provided to indicate whether the dialog list is currently syncing data with the server. <Frame> <img alt="Sync State Indication" /> </Frame> ### Usage To use the `DialogsListView` component in your SwiftUI view, follow these steps: Create an instance of `DialogsViewModel `with the appropriate `DialogsRepository` instance. Define the content views for the list view and the detail view for individual dialogs. Customize the appearance and behavior of the `DialogsListView` using the available configuration options. Embed the `DialogsListView` struct in your SwiftUI view hierarchy. Here's an example of how to use the `DialogsViewModel` feature: The dialogs list shows the complete list of group and private dialogs of which the current user is a member. After following the steps described in the [Get start](/ui-kits/android-uikit-send-your-first-message#get-started) section, you can display and manage the list of dialogs. ## Private Dialog screen ### Overview A private dialog is a chat that allows close interaction between two users. To participate in this type of dialog, you can create a dialog with your opponent or your opponent can create a dialog with you. The QuickBlox iOS UIKit offers comprehensive support for various message types to be sent within private dialogs. Users can send plain text messages, file messages, and media content like photos and videos seamlessly. Once delivered, these messages are automatically organized and grouped within the dialog. The grouping is based on time in minutes and date, allowing for a clear and organized display of the messages in the dialog's chat history. With this functionality, users can engage in effective communication, sharing a wide range of content types, and easily navigate through the chat history based on the chronological grouping of messages. This feature enhances the overall messaging experience and ensures a structured presentation of messages in private dialogs. <Frame> <img alt="UIKit PR" /> </Frame> ### Features * Sending messages allows users to send messages, images, videos and files in message input mode. * Delivery Notice displays the delivery notification status of a message in a dialog. * Read notification displays the status of receiving a read message in a dialog. * Typing indicator displays whether another user is typing a message in the dialog. ### Function List * `sendMessage(\_ text: String)` Sending various types of messages. * `handleOnSelect(attachment: AttachmentAsset)` Preparing and Sending Messages with Attachments. * `sendTyping()` Sending signals while the User is composing a message. The user actively interacts with the message input interface specific to this chat session (for example, by entering text in the input area of the chat window). * `sendStopTyping(`) Sending signals when the User has been writing but has now stopped. The user wrote but did not interact with the message input interface for a short period of time (for example, 30 seconds). * `handleOnAppear(\_ message: MessageItem)` Processing a message that appears from a dialog participant. * `startRecording()` Start recording a voice message. * `stopRecording()` Stop recording a voice message. * `deleteRecording()` Delete a voice message. * `playAudio(\_ audioData: Data, action: MessageAttachmentAction)` Play audio files, including voice messages. * `stopPlayng()` Stops playback of the audio file. * `unsubscribe()` Unsubscribe from receiving signals from publishers. ### Usage ### List Component The "Private Dialog screen" function uses a ListComponent to provide information about the message exchange. The ListComponent contains various `MessageRowView` types, each representing a different kind of message, such as simple text messages, file messages, and multimedia content such as photos and videos. This screen also contains a field for entering the text of the message, recording a voice message and accessing the Photo Gallery and Files. ### View model The "Private Dialog screen" function uses the `DialogViewModel` class as the view model, which conforms to the `DialogViewModelProtocol`. The view model handles the presentation logic and messaging and managing chat history data. It provides published properties for dialog-related information and chat interaction methods such as sending messages and interacting with features such as the Typing indicator. ## Private Dialog info screen ### Overview The "Private Dialog info" function in the QuickBlox iOS UIKit provides a user-friendly interface for viewing chat information. It allows users to access information related to a particular chat, such as the name and avatar of the participant. In addition, users can log out of the chat. <Frame> <img alt="UIKit Info" /> </Frame> ### Features * Display important information about the chat, including the participant's name and avatar. * Allow the user to log out of the chat. ### Function List * deleteDialog: Delete chat. ### Usage The "Private Dialog info" function is integrated into the QuickBlox iOS UIKit and users can easily access it from the chat interface. Users can go to the chat information screen to view chat details. ### List Component The "Private Dialog info" function uses a ListComponent to provide a structured view of chat information and actions. The ListComponent contains various segments, each representing a different chat action, such as searching the dialog, leaving the chat, and managing notifications. ### View model The Chat Info function uses the `DialogInfoViewModel` class as a view model that conforms to the `DialogInfoProtocol`. The view model handles the presentation logic and data management for the chat information. It provides published properties for dialog-related information and chat interaction methods, such as deleting a chat. ## Create Private Dialog screen ### Overview The "Create Private Dialog" function is a key feature of the QuickBlox iOS UIKit that allows users to create new private dialogs with the participants they want. It provides an easy and efficient way to create private conversations with specific participants. <Frame> <img alt="Create PR" /> </Frame> ### Features * Search and Filter: Users can search for specific people to add to a private chat, and this feature will automatically filter and display the relevant results. * Real-time Updates: This feature uses aggregation to update the displayed list of users in real time based on search criteria. * Selecting the necessary chat participant and manipulating them. * Create a private chat with the necessary chat participant. ### Usage To use the "Create Private Dialog" function in your QuickBlox iOS app, follow these steps: * Create a `modeldDialog: Dialog` object representing the private chat you will set up for the desired users on this screen to complete the setup of the new dialog and create it. * Instantiate the `CreateDialogViewModel` by passing a `modeldDialog: Dialog` object as a parameter. * Monitor search property changes to dynamically update the displayed list of users based on search criteria. * To set up a user for a private chat, select the user from the displayed list by clicking on their cell by calling the `handleOnSelect(\_ item: UserItem)` method and you will notice that the selection is confirmed with a checkbox. * To remove a user from the member list, click the user by calling the `handleOnSelect(\_ item: UserItem)` method and the check box will return to its default state. * Select the required user and call the `createDialog()` method by clicking on the "Create" button. * After successfully creating a new private chat, it will automatically go to the Private Dialog screen with that dialog. ### ListComponent The user interface of the "Create Private Dialog" function consists of a ListComponent that displays a list of users that can be configured for a private chat. The ListComponent is dynamic and updates in real time depending on the search criteria. This allows users to scroll through the available users and select the right user to install in the conversation. ### View Model `CreateDialogViewModel` is the view model responsible for managing the screen of the private creation dialog. It handles user interaction, search functionality, and updates the displayed list of users. The view model also manages asynchronous tasks with `Task\<Void, Never>` and `AnyCancellable` in `Combine`. It provides efficient and secure handling of UI updates and background operations. `CreateDialogViewModel` is equipped with a set of methods, including sync() to synchronize dialog members, `handleOnSelect(\_ item: UserItem)` to set user to a private dialog, and createDialog() to create a private chat. ## Dialog Type screen ### Overview The "Dialog Type" function is a key feature of the QuickBlox iOS UIKit that allows users to select the desired conversation type to create a new dialog. This can be a private conversation or a group conversation. <img /> ### Features * Select the desired dialog type to create a new dialog. It can be a personal conversation or a group conversation. ### Usage To use the "Dialog Type" function in your QuickBlox iOS app, follow these steps: * Select the desired dialog type to create a new dialog. If a Private Dialog is selected, the Create Private Dialog screen will open, and if a Group Dialog is selected, the Group Dialog name screen will open. ### Components The "Dialog Type" function UI onsists of a DialogTypeBar and is a segmented control for selecting the desired dialog type to create a new dialog. This allows users to select the desired type of conversation to create a new conversation. ## Group Dialog screen ### Overview A "Group Dialog" is a chat that allows close interaction between a specific group of users. To participate in this type of dialogue, you can create a group dialogue with a group of opponents. QuickBlox iOS UIKit offers comprehensive support for various types of messages to send in group conversations. Users can seamlessly send simple text messages, file messages, and multimedia content such as photos and videos. Once delivered, these messages are automatically organized and grouped in a dialog box. The grouping is based on time in minutes and date, which allows messages to be displayed clearly and organized in the dialog box's chat history. With this feature, users can engage in effective communication, share a wide range of content types, and easily navigate through chat history based on the chronological grouping of messages. This feature improves the overall messaging experience and provides a structured view of messages in group dialogs. <img /> ### functions * Sending messages allows users to send messages, images, videos and files in message input mode. * Delivery Notice displays the delivery notification status of a message in a dialog. * Read notification displays the status of receiving a read message in a dialog. * Typing indicator displays whether another user is typing a message in the dialog. ### Function List * sendMessage(\_ text: String) Sending various types of messages. * handleOnSelect(attachment: AttachmentAsset) Preparing and Sending Messages with Attachments. * sendTyping() Sending signals while the User is composing a message. The user actively interacts with the message input interface specific to this chat session (for example, by entering text in the input area of the chat window). * handleOnAppear(\_ message: MessageItem) Processing a message that appears from a dialog participant. * sendStopTyping() Sending signals when the User has been writing but has now stopped. The user wrote but did not interact with the message input interface for a short period of time (for example, 30 seconds). * startRecording() Start recording a voice message. * stopRecording() Stop recording a voice message. * deleteRecording() Delete a voice message. * playAudio(\_ audioData: Data, action: MessageAttachmentAction) Play audio files, including voice messages. * stopPlayng() Stops playback of the audio file. * unsubscribe() Unsubscribe from receiving signals from publishers. ### Usage ### List Component The "Group Dialog screen" function uses a ListComponent to provide information about the message exchange. The ListComponent contains various MessageRowView types, each representing a different kind of message, such as simple text messages, file messages, and multimedia content such as photos and videos. This screen also contains a field for entering the text of the message, recording a voice message and accessing the Photo Gallery and Files. ### View model The "Group Dialog screen" function uses the DialogViewModel class as the view model, which conforms to the DialogViewModelProtocol. The view model handles the presentation logic and messaging and managing chat history data. It provides published properties for dialog-related information and chat interaction methods such as sending messages and interacting with features such as the Typing indicator. ## Group Dialog info screen ### Overview The "Chat Info" function in the QuickBlox iOS UIKit provides a user-friendly interface to view and manage chat information. It allows users to access details related to a specific chat, such as the chat name, members, avatar, and other relevant data. In addition, users can perform various actions such as viewing members and leaving the chat. The chat creator can edit the chat name and change the chat avatar. <Frame> <img alt="UIKit Info" /> </Frame> ### Features * Display essential information about a chat, including chat name and avatar. * Allow users to edit the chat name and update the chat avatar. Available to the chat creator. <Frame> <img alt="Edit Group Dialog" /> </Frame> * Provide options to view and manage chat members. * Support both private and group chat information display. * Allow the user to log out of the chat. ### Function List * updateDialog: Update the chat's information, such as name and avatar. * handleOnSelect(attachmentAsset:): Handle the selection of an attachment asset (for example,, image) for updating the chat avatar. * removeExistingImage: Remove the existing chat avatar. * removeDialogAvatar: Remove the chat avatar and update the chat information. * handleOnSelect(newName:): Handle the selection of a new chat name and update the chat information. * deleteDialog: Delete the chat. ### Usage The "Chat Info" feature is integrated into the QuickBlox iOS UIKit and can be easily accessed by users from the chat interface. Users can navigate to the chat info screen to view and manage the chat details. The UIKit provides different views for group chats based on the user's permission to edit the chat. ### ListComponent The "Chat Info" function utilizes a ListComponent to present the chat information and actions in a structured manner. The ListComponent contains various segments, each representing different chat actions, such as viewing members, searching in the dialog, leaving the chat, and managing notifications. ### View Model The "Chat Info" function uses a DialogInfoViewModel class as its View Model, which conforms to the DialogInfoProtocol. The View Model handles the presentation logic and data management for the chat information. It provides published properties for dialog-related information and methods to interact with the chat, such as updating the chat, handling user selections, and deleting the chat. ## Members (Remove users) screen ### Overview The "Members (Remove users)" function is a key feature of the QuickBlox iOS UIKit that allows users to remove participants from a group chat. It provides a user-friendly interface to manage the members of a group chat and control who can participate in the conversation. <Frame> <img alt="Remove User" /> </Frame> ### Features: * Display Participants: The function displays a list of participants in the group chat, showing their names and other relevant details. * User Removal: Users with administrative privileges can select and remove participants from the chat, ensuring efficient chat management. * Alert Confirmation: When an admin attempts to remove a user, an alert prompt is presented, confirming their intent before the action is executed. * Add Members: The function includes an option to add new members to the group chat through a navigation link to the "Add Members" screen. ### Function List: * removeUserFromDialog(): This function is responsible for removing the selected user from the group chat. It triggers an update to the dialog with the updated participant list, effectively removing the user from the chat. ### Usage: The "Members (Remove users)" function is integrated into the QuickBlox iOS UIKit's group chat management system. Users with admin privileges can access this functionality from the group chat screen. Upon selecting a user to remove, an alert will appear, requesting confirmation for the removal action. Once confirmed, the selected user will be removed from the group chat. ### ListComponent: The RemoveUserListView is a SwiftUI component responsible for displaying the list of participants in the group chat and enabling the user to select users for removal. It includes options for handling user selection and pagination for large participant lists. ### View Model: The MembersDialogViewModel class serves as the view model for the "Members (Remove users)" function. It manages the state of the view, including the list of displayed users, the selected user to be removed, and the current search text. The view model handles interactions with the QuickBlox backend, ensuring data synchronization, fetching user information, and updating the dialog with the latest changes. ## Add Members screen ### Overview The "Add Members" function is a key feature of the QuickBlox iOS UIKit that allows users to add new members to a group chat. It provides an easy and efficient way to expand group conversations by inviting new participants to join the conversation. <Frame> <img alt="Add User" /> </Frame> ### Features * Search and Filter: Users can search for specific individuals to add to the group chat, and the function automatically filters and displays relevant results. * Real-time Updates: The function leverages Combine, allowing for real-time updates of the displayed user list based on the search criteria. ### Usage To use the "Add Members" function in your QuickBlox iOS app, follow these steps: * Instantiate a Dialog object representing the group chat you want to add users to. * Create an instance of the AddMembersDialogViewModel, passing the Dialog object as a parameter. * Observe changes to the search property to dynamically update the displayed user list based on the search criteria. * To add a user to the group chat, select a user from the displayed list, and call the addSelectedUser() method. ### ListComponent The "Add Members" function UI consists of a ListComponent displaying the list of users that can be added to the group chat. The ListComponent is dynamic and updates in real-time based on the search criteria. It allows users to scroll through the available users and select the desired individuals to add. ### View Model The AddMembersDialogViewModel is the view model responsible for controlling the "Add Members" dialog. It handles user interactions, search functionality, and updates to the displayed user list. The view model also manages asynchronous tasks using Task`<Void, Never>` and Combine's AnyCancellable. It ensures efficient and safe handling of UI updates and background operations. The AddMembersDialogViewModel is equipped with a set of methods, including sync() to synchronize dialog members, displayDialogMembers() to filter users based on the search query, and addSelectedUser() to add a selected user to the group chat. ## New Dialog screen ### Overview The "New Dialog" feature is a key feature of the QuickBlox iOS UIKit that allows users to create new group chats with the with the name already required and an optional avatar. ### Features * Dialog Avatar Selection: Users can select a desired image from the Photo Gallery and set it as the dialog avatar. This is an optional option to create a dialog. <Frame> <img alt="Dialog Avatar Selection" /> </Frame> * Naming the Dialog: Users can give the desired name to the new dialog. This is a required option to create a dialog. <Frame> <img alt="Naming the Dialog" /> </Frame> ### Usage To use the "New Dialog" function in your QuickBlox iOS app, follow these steps: * Select an avatar for the new dialog from the photo gallery and set it as the dialog's avatar. This is an optional parameter to create a dialog. * Give the desired name to the new dialog box by typing it in the input field. The name must match these parameters: "Use alphanumeric characters and spaces in a range from 3 to 60. Cannot contain more than one space in a row.". This is a required option to create a dialog. * Create a modelDialog: Dialog object representing the group chat to complete the setup of the new dialog. ### Components The user interface of the "New Dialog" function consists of a DialogPhoto to select an avatar for the new dialog from the photo gallery, and a DialogNameTextField with which you can give the desired name to the new dialog. ### View Model NewDialogViewModel is the view model responsible for managing the "New Dialog" screen. It handles the user interaction to select a photo for the dialog's avatar and enter the dialog's name. The view model also manages asynchronous tasks with Task`<Void, Never>` and AnyCancellable in Combine. It provides efficient and secure handling of UI updates and background operations. The NewDialogViewModel is equipped with a set of methods, including handleOnSelect(attachmentAsset: AttachmentAsset) to set the dialog avatar photo, removeExistingImage() to remove the dialog avatar photo, and createDialogModel() to create a group chat model. ## Create Group Dialog screen ### Overview The "Create Group Dialog" feature is a key feature of the QuickBlox iOS UIKit that allows users to create new group chats with the members they want. It provides an easy and efficient way to create group conversations with specific participants. <Frame> <img alt="Create Group" /> </Frame> ### Features * Search and Filter: Users can search for specific people to add to a group chat, and this feature will automatically filter and display the relevant results. * Real-time Updates: This feature uses aggregation to update the displayed list of users in real time based on search criteria. * Selecting the necessary chat participants and manipulating them. * Create a group chat with the right members. ### Usage To use the "Create Group Dialog" function in your QuickBlox iOS app, follow these steps: * Create a modeldDialog: Dialog object representing the group chat you will set up for the desired users on this screen to complete the setup of the new dialog and create it. * Instantiate the CreateDialogViewModel by passing a modeldDialog: Dialog object as a parameter with the name already required and an optional avatar. * Monitor search property changes to dynamically update the displayed list of users based on search criteria. * To set up a user for a group chat, select the user from the displayed list by clicking on their cell by calling the handleOnSelect(\_ item: UserItem) method and you will notice that the selection is confirmed with a checkbox. * To remove a user from the member list, click the user by calling the handleOnSelect(\_ item: UserItem) method and the check box will return to its default state. * Select the required users and call the createDialog() method by clicking on the "Create" button. * After successfully creating a new group chat, it will automatically go to the Group Dialog screen with that dialog. ### ListComponent The user interface of the "Create Group Dialog" function consists of a ListComponent that displays a list of users that can be configured for a group chat. The ListComponent is dynamic and updates in real time depending on the search criteria. This allows users to scroll through the available users and select the right people to install in the conversation. ### View Model CreateDialogViewModel is the view model responsible for managing the screen of the group creation dialog. It handles user interaction, search functionality, and updates the displayed list of users. The view model also manages asynchronous tasks with Task`<Void, Never>` and AnyCancellable in Combine. It provides efficient and secure handling of UI updates and background operations. CreateDialogViewModel is equipped with a set of methods, including sync() to synchronize dialog members, handleOnSelect(\_ item: UserItem) to set users to a group dialog, and createDialog() to create a group chat. # Send your first message Source: https://docs.quickblox.com/ui-kits/ios-uikit-send-your-first-message ## Requirements The minimum requirements for QuickBlox UIKit for iOS are: * iOS 15.0 * Xcode 14 ## Before you begin Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. Create the app clicking New app button. Configure the app. Type in the information about your organization into corresponding fields and click Add button. Go to Dashboard => YOUR\_APP => Overview section and copy your Application ID, Authorization Key, Authorization Secret, and Account Key . <Warning> Users are required for messaging! To successfully create a private p2p dialogue or group dialogue and a full-fledged messaging, you need more users besides the one under which you logged into the QuickBlox system. If you just created a new application in your QuickBlox account, then there will be no users in this application. You can read how to add new users using the admin panel of your account at [this link](/docs/users-dashboard#create-user). There is also an easier way to add a new user - just run our [UIKitSample](/ui-kits/ios-uikit-send-your-first-message#sample) and register another new user on the SignUp screen </Warning> ## Install QuickBlox UIKit To add QuickBlox UIKit to your project using SPM, you can follow these steps: Open your Xcode project and navigate to File > Swift Packages > Add Package Dependency. In the search bar, enter the QuickBlox UIKit repository URL: [https://github.com/QuickBlox/ios-ui-kit.git](https://github.com/QuickBlox/ios-ui-kit.git) and click Add Package. Xcode will then fetch the SDK and you can add it to your project by clicking Add Package. You can then import QuickBloxUIKit modules into your code and use its API. ```Swift Swift theme={null} import QuickBloxUIKit ``` ## Init QuickBlox SDK To init QuickBlox SDK you need to pass Application ID, Authorization Key, Authorization Secret, and Account Key to the initWithApplicationId() method. ```Swift Swift theme={null} Quickblox.initWithApplicationId(92, authKey: "wJHdOcQSxXQGWx5", authSecret: "BTFsj7Rtt27DAmT", accountKey: "7yvNe17TnjNUqDoPwfqp") ``` <Warning> Security It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend. Instead you can [initialize QuickBlox SDK without Authorization Key and Secret](/sdks/ios-setup#initialize-quickblox-sdk-without-authorization-key-and-secret) </Warning> ## Authentication and show QuickBlox UIKit screen Before using the QuickBlox iOS UIKit you need to authenticate users in the QuickBlox system. You can read more about different ways of authentication by [this link](/sdks/ios-authentication). In our example, we will show how to authenticate a user with a username and password, then log in to QuickBlox Chat ([Connect to Chat](/sdks/ios-chat-connection)). Upon successful authorization, the UIKit’s Dialogues screen will automatically open. <Note> If you need to handle the exit event from the QuickBlox iOS UIKit, you can do this in a closure onExit: </Note> ```Swift Swift theme={null} import QuickBloxUIKit import Quickblox struct ContentView: View { @State private var isPresented = false var body: some View { Button("Present!") { authenticateUser() } .fullScreenCover(isPresented: $isPresented) { // show Dialogs screen QuickBloxUIKit.dialogsView(onExit: { // Handling an event when exiting the QuickBloxUIKit for example, disconnect and logout }) } } func authenticateUser() { QBRequest.logIn(withUserLogin: "userLogin", password: "userPassword", successBlock: { (response, user) in // Block with response and user instances if the request is succeeded. isPresented.toggle() print("Success login") }, errorBlock: { (response) in // Block with response instance if the request is failed. }) } } ``` ## Sample ### Download and setup * To use the UIKitSample you need a QuickBlox account. Register a new account by following the step-by-step guide in this [section](/ui-kits/ios-uikit-overview) or use an existing one. * Download the [UIKitSample](https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/sample-ui-kit) and open it in Xcode by double-clicking the .xcodeproj file. * In UIKitSample, go to the Connect class and fill in the appropriate fields: ```Swift swift theme={null} class Connect: ObservableObject { @Published var state: ConnectState = .waiting init(state: ConnectState = .disconnected) { self.state = state Quickblox.initWithApplicationId(0, // Your_Application_ID authKey: "", // Your_Authorization_Key authSecret: "", // Your_Authorization_Secret accountKey: "") // Your_Account_Key QBSettings.carbonsEnabled = true QBSettings.autoReconnectEnabled = true } ... } ``` That's all you need to start using UIKit sample. ### Features **UIKit Sample contains screens:** 1. **LoginScreen.** Before using the QuickBlox iOS UIKit, you need to authenticate users to the QuickBlox system. If the user is not already signed in to the app, they will be able to sign in as a user on this screen using their username, display name, and password. Upon successful registration, you will be automatically logged into QuickBlox and connected to the QuickBlox chat. Once the user is logged in, UIKit immediately opens when the application is opened, or if the user is logged out, they can log into the sample using their username and password. The connection to the QuickBlox chat will be established automatically. <Warning> Users are required for messaging. To successfully create a private p2p dialogue or group dialogue and a full-fledged messaging, you need more users besides the one under which you logged into the QuickBlox system. If you just created a new application in your QuickBlox account, then there will be no users in this application. To add a new user, simply run this sample and register another user on the SignUp screen. </Warning> The struct LoginScreen contains a boolean variable showChangeColorTheme with a default value of false: ```Swift Swift theme={null} let showChangeColorTheme: Bool = false ``` with this setting, the UIKit's screen will open immediately after the user successfully logs into the system: ```Swift swift theme={null} var body: some View { container() .if(showChangeColorTheme == false && connect.state == .connected, transform: { view in view.fullScreenCover(isPresented: $connect.isConnected) { QuickBloxUIKit.dialogsView(onExit: { // Handling an event when exiting the QuickBloxUIKit for example, disconnect and logout }) } }) } ``` If you want to see an option with the ability to change the theme by the user, then set this variable to true: ```Swift Swift theme={null} let showChangeColorTheme: Bool = true ``` And then, after successful authorisation of the user in the system, the Choice of Color Theme Screen will open. 1. **Choice of color theme.** On this screen, the user can select a color theme for the whale. After the user selects the color theme he needs, the Enter the chat screen will open. 2. **Enter the chat.** ```Swift Swift theme={null} struct EnterToChatView: View { @ObservedObject var theme: AppTheme @State var isPresented = false init(theme: AppTheme) { self.theme = theme QuickBloxUIKit.settings.theme = theme } var body: some View { ZStack { // Button to enter the QuickBlox iOS UIKit. Button(action: { self.isPresented = true }, label: { Text("Enter to SwiftUIChat") }) } .fullScreenCover(isPresented: $isPresented) { QuickBloxUIKit.dialogsView(onExit: { // Handling an event when exiting the QuickBloxUIKit for example, disconnect and logout }) } } } ``` Pressing the "Enter to SwiftUIChat" button will open the Dialogs screen of QuickBlox iOS UIKit. # Overview Source: https://docs.quickblox.com/ui-kits/overview Use pre-built UI elements to create a chat interface quickly and easily. <CardGroup> <Card title="iOS" icon="apple"> <a href="/ui-kits/ios-uikit-overview"> <Icon icon="circle-info" /> Overview </a> <br /> <a href="https://github.com/QuickBlox/ios-ui-kit"> <Icon icon="github" /> View on GitHub </a> </Card> <Card title="Android" icon="android"> <a href="/ui-kits/android-uikit-overview"> <Icon icon="circle-info" /> Overview </a> <br /> <a href="https://github.com/QuickBlox/android-ui-kit"> <Icon icon="github" /> View on GitHub </a> </Card> <Card title="React" icon="react"> <a href="/ui-kits/react-uikit-overview"> <Icon icon="circle-info" /> Overview </a> <br /> <a href="https://github.com/QuickBlox/react-ui-kit"> <Icon icon="github" /> View on GitHub </a> </Card> <Card title="Flutter" icon="flutter"> <a href="/ui-kits/flutter-uikit"> Coming Soon </a> </Card> </CardGroup> # AI Features Source: https://docs.quickblox.com/ui-kits/react-uikit-ai-features AI Features base on OpenAI GPT and QuickBlox AI Agent ## Overview Starting from version 0.2.0 of React UIKit, the AI functionality is enabled by default and based on OpenAI's chat GPT. The QuickBlox AI for React UIKit provides a range of features to enhance the chat experience. With essential messaging functionalities such as answer assistant, users can engage in more interactive conversations. **Supported features** | Name | Description | | ---------------- | ------------------------------------------------------------------------------------------- | | AI Answer Assist | Provides answers based on chat history to selected message. | | AI Translate | Provides translation based on chat history to selected incoming message. | | AI Rephrase | Provides rephrase in selected tone based on chat history to inputed message before sending. | ## Requirements The minimum requirements for using AI features are: * JS QuickBlox SDK v2.15.5 * React v.18.0 * TypeScript v.4.9.3 * React UIKit v0.2.0 ## AI Answer Assist QuickBlox provides answer assistant functionality that helps users effortlessly send various answers considering chat history. <Frame> <img /> </Frame> ### How to use in React UIKit In React UIKit version greater then 0.4.0 with JS QuickBlox SDK v2.17.0 to enable QuickBlox AI answer assistant you should set up the `AIAnswerAssistWidgetConfig` in the `configAIApi` segment of the `QBConfig.ts` file to provide your Smart ChatAssist ID from your QuickBlox admin panel. **Review your AI Agent** To get **ID** of AI Agent to use it as `smartChatAssistantId ` param follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Agent** page. 2. Choose the ID of the AI Agent you want to update and click on it. 3. Edit the AI Agent settings. 4. Click the Save button to save changes. <img alt="" /> To utilize the QuickBlox **AI Answer Assist React hook** to communicate directly with the OpenAI API , you should configure the **AIAnswerAssistWidgetConfig** section in the configuration file, as shown in the code snippet provided below. ```TypeScript TypeScript theme={null} export const QBConfig = { credentials: { //.. other section data }, configAIApi: { AIAnswerAssistWidgetConfig: { smartChatAssistantId:: 'sk-k2...LfK', // Should use your own data from your admin panel }, }, appConfig: { //... other section data }, }; ``` <Warning> **Warning** The code snippets below are not recommended for use with React UIKit version greater than 0.4.0, because they will be deprecated. In the modern version of our React UIKit, you do not have the ability to make AI Assist customizations. </Warning> Enabling QuickBlox AI answer assistant in React UIKit **version less then 0.4.0** involves just 2 straightforward actions: 1. Set up the `AIAnswerAssistWidgetConfig` in the `configAIApi` segment of the `QBConfig.ts` file to provide your API Key. 2. Optionally, initialize the `AIAssist` property within the` QuickBloxUIKitDesktopLayout` (or `MessageView`) component for customizing the appearance or logic of the AI Assist component. There are two ways to use the QuickBlox AI Assist answer: 1. **Direct**: Access Open AI directly from your client code. The `AIAssist` component use a raw Open AI token. This is suitable for development or demos, but it's not the recommended approach for production due to security considerations. 2. **Proxy**: Access Open AI through a proxy server. The `AIAssist` component use QuickBlox user session token with a proxy server. This is the recommended method for production environments, ensuring a more secure and controlled setup. **Directly using configuration** To utilize the QuickBlox **AI Answer Assist React hook** to communicate directly with the OpenAI API , you should configure the **AIAnswerAssistWidgetConfig** section in the configuration file, as shown in the code snippet provided below. The token should be stored in the `apiKey` property in the `configAIApi` section in the `QBConfig.ts` file. This contains information about configurations for **[all AI features](/ui-kits/react-uikit-ai-features#overview)** . After that the`AIAssist` component will communicate directly with the OpenAI API. ```TypeScript TypeScript theme={null} export const QBConfig = { credentials: { //.. other section data }, configAIApi: { AIAnswerAssistWidgetConfig: { apiKey: 'sk-k2...LfK', // Should use your own API key useDefault: true, //... other section data }, }, appConfig: { //... other section data }, }; ``` | Parameter name | Type | Description | | -------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | apiKey | String | This field should hold your actual API key that you'll receive from the service provider (in this case, OpenAI). This key is used to authenticate your requests to the AI service. | | useDefault | Boolean | This setting seems to indicate whether you want to use default settings for the AI answer assistance widget. If set to true, it implies that the default settings will be applied. | **Proxy using configuration** To utilize the QuickBlox **AI Answer Assist React hook** to communicate through a proxy server (not directly) with OpenAI API, you should configure the **AIAnswerAssistWidgetConfig** section in the configuration file, as shown in the code snippet provided below. The fields `api`, `servername`, `port`, and `sessionToken` need to be configured within the proxyConfig section for communication with the proxy server. ```TypeScript TypeScript theme={null} export const QBConfig = { credentials: { //... other section data }, configAIApi: { AIAnswerAssistWidgetConfig: { apiKey: '', // must be empty useDefault: true, proxyConfig: { api: 'v1/chat/completions', servername: 'https://myproxy.com', port: '', sessionToken: '', }, }, }, appConfig: { //... other section data }, }; ``` | Parameter name | Type | Description | | -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ | | api | String | The specific API endpoint for AI completions. In default, it's set to v1/chat/completions. You should provide your url. | | serverName | String | The base URL for API requests. In default, it's set to '[https://api.openai.com/'](https://api.openai.com/'). You should provide your url. | | sessionToken | String | Your should provide the QuickBlox user session token by default if you are using our proxy server. | | port | String | Default should be empty. | <Tip> We recommend using our proxy server, which you can access through this [link](https://github.com/QuickBlox/qb-ai-assistant-proxy-server). Please ensure that you are using the latest release. </Tip> **AI Answer Assist customization** You have the ability to control AI Answer Assist from code as follows: set it to enabled/disabled, customize the appearance of the component icon, and modify its operational logic. To enable or disable AIAssist use the following code: ```JSX JSX theme={null} <QuickBloxUIKitDesktopLayout theme={new CustomTheme()} AIAssist={{ enabled: true, default: true }} /> ``` The `QuickBloxUIKitDesktopLayout` is a layout component located within the provider section. We explored an example of its usage in our UIKit documentation, specifically in the [Send your First Message](/ui-kits/react-uikit-send-your-first-message#step-5-configure-quickbloxuikitprovider) section. To customize the appearance of the component icon, you should create your own React custom hook that must return an object of the AIMessageWidget interface and update the code within the renderWidget method. For modifying its operational logic, you should create your own React custom hook that must return an object of the AIMessageWidget interface and update the code within the textToWidget method. The interface `AIMessageWidget` has next structure: | Parameter name | Type | Description | | -------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | renderWidget | Object function | The renderWidget provides the default appearance of the widget. This is a method that returns a JSX element (React component). It's responsible for rendering the widget, and depending on the presence of errors, it might display an error icon or a support icon. You should change it to implement your own icon. | | textToWidget | Object function | The textToWidget provides the default logic of the widget. This is a function that takes a string (text) and context (in this case, an array of chat messages) but doesn't return anything (void). This function is used to pass text into the widget. You should change it to implement your logic. | | textToContent | String | The textToContentprovides the output of the default algorithm implementation. This is a string field representing content for the text widget. It's likely to be used to display the response obtained from AI in the text widget. | Example code of custom hook: ```TypeScript TypeScript theme={null} interface MessageWidgetProps { servername: string; api: string; port: string; sessionToken: string; } export default function useDefaultAIAssistAnswerWidgetWithOpenAIApi({ servername, api, port, sessionToken, }: MessageWidgetProps): AIMessageWidget { const [errorMessage, setErrorMessage] = useState<string>(''); const [textFromWidgetToContent, setTextFromWidgetToContent] = useState(''); const renderWidget = (): JSX.Element => { if (errorMessage && errorMessage.length > 0) { const errorsDescriptions: | { title: string; action: () => void }[] | undefined = []; return ( <ErrorMessageIcon errorMessageText={errorMessage} errorsDescriptions={errorsDescriptions} /> ); } // you can customize your own Widget Icon markup return <AIWidgetIcon applyZoom color="green"/>; }; const textToWidget = (value: string, context: IChatMessage[]): void => { if (value && value.length > 0) { getData(value, context as ChatCompletionRequestMessage[]).then( (data) => { setTextFromWidgetToContent(data); }, ); } }; async function getData( textToSend: string, dialogMessages: ChatCompletionRequestMessage[], ): Promise<string> { let answerSuggetion = ''; if (textToSend.length === 0) return answerSuggetion; // your logic to use openai return answerSuggetion; } return { textToContent: textFromWidgetToContent, renderWidget, textToWidget, }; } ``` To set up your own realization you should use the property `AIAssist` in`QuickBloxUIKitDesktopLayout` or in `MessageView` components like this: Let's modify the QuickBloxUIKitDesktopLayout component from ["Send your first message" page](/ui-kits/react-uikit-send-your-first-message#step-4-updating-the-project-structure-according-to-your-needs) : ```JSX JSX theme={null} import React from 'react'; import * as QB from "quickblox/quickblox"; import { LoginData, QuickBloxUIKitProvider, qbDataContext, RemoteDataSource, useQBConnection, QuickBloxUIKitDesktopLayout, } from 'quickblox-react-ui-kit'; function App() { const currentUser: LoginData = { userName: 'YOUR_REGISTRED_USER_NAME', password: 'YOUR_REGISTRED_USER_PASSWORD', }; const apiKey = 'sk-9aXs...ZFqhU'; // use your Api Key const openAIConfiguration: Configuration = new Configuration({ apiKey, }); const openAIApi: OpenAIApi = new OpenAIApi(openAIConfiguration); const defaultAIAnswerToMessageWidget = useDefaultAIAssistAnswerWidgetWithOpenAIApi({ ...QBConfig.configAIApi.AITranslateWidgetConfig.proxyConfig, openAIApi, }); return ( <QuickBloxUIKitProvider maxFileSize={100 * 1000000} accountData={{ ...QBConfig.credentials }} qbConfig={{ ...QBConfig }} loginData={{ login: currentUser.login, password: currentUser.password, }} > <div className="App"> <QuickBloxUIKitDesktopLayout theme={new DefaultTheme()} AIAssist={{ enabled: true, default: false, AIWidget: defaultAIAnswerToMessageWidget, }} /> </div> </QuickBloxUIKitProvider> ); } export default App; ``` in MessageView component se detail information on this [page](/ui-kits/react-uikit-screen-modules#group-and-private-chats-screen) : ```JSX JSX theme={null} <DesktopLayout theme={theme} dialogsView={ <DialogsComponent // ... /> } dialogMessagesView={ <MessagesView subHeaderContent={<CompanyLogo/>} upHeaderContent={<CompanyLogo/>} dialogsViewModel={dialogsViewModel} onDialogInformationHandler={informationOpenHandler} theme={theme} AIAnswerToMessage={defaultAIAnswerToMessageWidget} /> } dialogInfoView={ <DialogInformation // ... /> } /> ``` <Note> You can compare the configuration of the QuickBloxUIKitDesktopLayout component with the AI Widget to the default view of the QuickBloxUIKitDesktopLayout component from the ["Send your first message" page](/ui-kits/react-uikit-send-your-first-message#step-4-updating-the-project-structure-according-to-your-needs) . </Note> ### How to use the feature as a standalone library You can use logic of the React hook features separately from our UI kit is possible by connecting the library as described below. The library qb-ai-answer-assistant contains QBAIAnswerAssistant object wich provide all nececry features. **install standalone library** QBAIAnswerAssistant can be installed using Node Package Manager. To include it in your JS based project, follow these command: ```PowerShell PowerShell theme={null} npm install qb-ai-answer-assistant ``` **add to own project** To use QBAIAnswerAssistant in your project, follow these steps: 1. Import the QBAIAnswerAssistant module: ```JavaScript JavaScript theme={null} import { ChatMessage } from 'qb-ai-core'; import { AIAnswerAssistantSettings, QBAIAnswerAssistant } from 'qb-ai-answer-assistant'; ``` 1. Create an array of `ChatMessage` objects representing the chat history: ```JavaScript JavaScript theme={null} let prompt = "YOUR_TEXT_TO_ANSWER_ASSIST"; let messages = [ {role: Role.other, content: "Hello, Bob!"}, {role: Role.me, content: "Hi, Jim!"}, {role: Role.me, content: prompt}, ] ``` or ```JavaScript JavaScript theme={null} let prompt = "YOUR_TEXT_TO_ANSWER_ASSIST"; let messages = [ {role: "assistant", content: "Hello, Bob!"}, {role: "user", content: "Hi, Jim!"}, {role: "user", content: prompt}, ] ``` 1. Call the `rephrase` method to generate translate using an API key: ```JavaScript JavaScript theme={null} const settings: AIAnswerAssistantSettings = QBAIAnswerAssistant.createDefaultAIAnswerAssistantSettings(); settings.apiKey = 'YOUR_OPEN_AI_API_KEY'; settings.organization = 'YOUR_ORGANIZATION';// might be empty settings.model = AIModel.gpt__3_5__turbo; return QBAIAnswerAssistant.createAnswer( promt, messages, settings, ); ``` Alternatively, you can use a QuickBlox user token and a proxy URL for more secure communication: ```JavaScript JavaScript theme={null} const settings: AIAnswerAssistantSettings = QBAIAnswerAssistant.createDefaultAIAnswerAssistantSettings(); settings.organization = 'YOUR_ORGANIZATION';// might be empty settings.model = AIModel.gpt__3_5__turbo; settings.token = 'YOUR_QUICKBLOX_USER_SESSION_TOKEN'; settings.serverPath = `https://your-proxy-server-url`; return QBAIAnswerAssistant.createAnswer( promt, messages, settings, ); ``` ## AI Translate QuickBlox offers translation functionality that helps users easily translate text messages in chat, taking into account the context of the chat history. <Frame> <img /> </Frame> ### How to use in React Kit In React UIKit greater than 0.4.0 with JS QuickBlox SDK v2.17.0, to enable QuickBlox AI Translate, you should set up the `AITranslateWidgetConfig` in the `configAIApi` segment of the `QBConfig.ts` file to provide your Smart ChatAssist ID from your admin panel. **Review your AI Agent** To get **ID** of AI Agent to use it as `smartChatAssistantId` param, follow the steps below: 1. Navigate to the **Dashboard => *YOUR\_APP* => AI Agent** page. 2. Choose the ID of the AI Agent you want to update and click on it. 3. Edit the AI Agent settings. 4. Click the Save button to save changes. <Frame> <img /> </Frame> To utilize the QuickBlox **AI Translate React hook** to communicate directly with the OpenAI API , you should configure the **AITranslateWidgetConfig** section in the configuration file, as shown in the code snippet provided below. ```TypeScript TypeScript theme={null} export const QBConfig = { credentials: { //.. other section data }, configAIApi: { AITranslateWidgetConfig: { smartChatAssistantId:: 'sk-k2...LfK', // Should use your own data from your admin panel }, }, appConfig: { //... other section data }, }; ``` <Warning> **Warning** The code snippets below are not recommended for use with React UIKit version greater than 0.4.0, because they will be deprecated. In the modern version of our React UIKit, you do not have the ability to make AI Translate customizations. </Warning> Enabling QuickBlox AI Translate in React UIKit **version less then 0.4.0** involves just 2 straightforward actions: 1. Set up the `AITranslateWidgetConfig` in the `configAIApi` segment of the `QBConfig.ts` file to provide your API Key. 2. Optionally, initialize the `AITranslate` property within the` QuickBloxUIKitDesktopLayout` (or `MessageView`) component for customizing the appearance or logic of the AI Translate component. There are two ways to use the QuickBlox AI Translate: 1. **Direct**: Access Open AI directly from your client code. The `AITranslate` component use a raw Open AI token. This is suitable for development or demos, but it's not the recommended approach for production due to security considerations. 2. **Proxy**: Access Open AI through a proxy server. The `AITranslate` component use QuickBlox user session token with a proxy server. This is the recommended method for production environments, ensuring a more secure and controlled setup. **Directly using configuration** To utilize the QuickBlox **AI Translate React hook** to communicate directly with the OpenAI API , you should configure the **AITranslateWidgetConfig** section in the configuration file, as shown in the code snippet provided below. The token should be stored in the `apiKey` property in the `configAIApi` section in the `QBConfig.ts` file. This contains information about configurations for **[all AI features](/ui-kits/react-uikit-ai-features#overview)** . AFter that the`AITranslate` component will communicate directly with the OpenAI API. ```TypeScript TypeScript theme={null} export const QBConfig = { credentials: { //.. other section data }, configAIApi: { AITranslateWidgetConfig: { apiKey: 'sk-k2...LfK', // Change to your real API key useDefault: true, defaultLanguage: 'English', languages: [ 'English', 'Spanish', 'French', 'Portuguese', 'German', 'Ukrainian', ], //... other section data }, }, appConfig: { //... other section data }, }; ``` | Parameter name | Type | Description | | --------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | apiKey | String | This field should hold your actual API key that you'll receive from the service provider (in this case, OpenAI). This key is used to authenticate your requests to the AI service. | | useDefault | Boolean | This setting seems to indicate whether you want to use the default settings for the AI answer assistance widget. If set to true, it implies that the default settings will be applied. | | defaultLanguage | String | This parameter specifies the default language you will use for translation. If this parameter is not set, the language from the system settings will be used (the language set in the system). If it's not possible to determine the system language, then English will be used by default. | | languages | Array String | This option allows you to set an array of alternative languages that can be used for translation. This list of languages will be displayed in the context menu before performing the text translation. | **Proxy using configuration** To utilize the QuickBlox **AI Translate React hook** to communicate through a proxy server (not directly) with OpenAI API, you should configure the **AITranslateWidgetConfig** section in the configuration file, as shown in the code snippet provided below. The fields `api`, `servername`, `port`, and `sessionToken` need to be configured within the proxyConfig section for communication with the proxy server. ```TypeScript TypeScript theme={null} export const QBConfig = { credentials: { //... other section data }, configAIApi: { AITranslateWidgetConfig: { apiKey: '', // must be empty useDefault: true, defaultLanguage: 'English', languages: [ 'English', 'Spanish', 'French', 'Portuguese', 'German', 'Ukrainian', ] proxyConfig: { api: 'v1/chat/completions', servername: 'https://myproxy.com', port: '', sessionToken: '', }, }, }, appConfig: { //... other section data }, }; ``` | Parameter name | Type | Description | | -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ | | api | String | The specific API endpoint for AI completions. By default, it's set to v1/chat/completions. You should provide your URL. | | serverName | String | The base URL for API requests. In default, it's set to '[https://api.openai.com/'](https://api.openai.com/'). You should provide your url. | | sessionToken | String | Your should provide the QuickBlox user session token by default if you are using our proxy server. | | port | String | Default should be empty. | <Tip> We recommend using our proxy server, which you can access through this [link](https://github.com/QuickBlox/qb-ai-assistant-proxy-server). Please ensure that you are using the latest release. </Tip> **AI Translate customization** You have the ability to control AI Translate from code as follows: set it to enabled/disabled, customize the appearance of the component icon, and modify its operational logic. To enable or disable AI Translate use the following code: ```JSX JSX theme={null} <QuickBloxUIKitDesktopLayout theme={new CustomTheme()} AITranslate={{ enabled: true, default: true }} /> ``` The `QuickBloxUIKitDesktopLayout` is a layout component located within the provider section. We explored an example of its usage in our UIKit documentation, specifically in the [Send your First Message](/ui-kits/react-uikit-send-your-first-message#step-5-configure-quickbloxuikitprovider) section. To customize the appearance of the component icon, you should create your own React custom hook that must return an object of the AIMessageWidget interface and update the code within the renderWidget method. For modifying its operational logic, you should create your own React custom hook that must return an object of the AIMessageWidget interface and update the code within the textToWidget method. The interface `AIMessageWidget` has next structure: | Parameter name | Type | Description | | -------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | renderWidget | Object function | The renderWidget provides the default appearance of the widget. This is a method that returns a JSX element (React component). It's responsible for rendering the widget, and depending on the presence of errors, it might display an error icon or a support icon. You should change it to implement your own icon. | | textToWidget | Object function | The texToWidget provides the default logic of the widget. This is a function that takes a string (text) and context (in this case, an array of chat messages) but doesn't return anything (void). This function is used to pass text into the widget. You should change it to implement your logic. | | textToContent | String | The textToContent provides the output of the default algorithm implementation. This is a string field representing content for the text widget. It's likely to be used to display the response obtained from AI in the text widget. | Example code of custom hook: ```TypeScript TypeScript theme={null} interface MessageWidgetProps { servername: string; api: string; port: string; sessionToken: string; } export default function useDefaultAITranslateWidget({ servername, api, port, sessionToken, }: MessageWidgetProps): AIMessageWidget { const [errorMessage, setErrorMessage] = useState<string>(''); const [textFromWidgetToContent, setTextFromWidgetToContent] = useState(''); const renderWidget = (): JSX.Element => { if (errorMessage && errorMessage.length > 0) { const errorsDescriptions: | { title: string; action: () => void }[] | undefined = []; return ( <ErrorMessageIcon errorMessageText={errorMessage} errorsDescriptions={errorsDescriptions} /> ); } // you can customize your own Widget Icon markup return <AIWidgetIcon applyZoom color="green"/>; }; const textToWidget = async ( textToSend: string, context: IChatMessage[], additionalSettings?: { [key: string]: any }, ): Promise<string> => { if (textToSend && textToSend.length > 0) { let prompt = "Your prompt text with textToSend data"; const { yourAdditionalParam } = additionalSettings || {}; // your logic return await getData(prompt, context).then((data) => { setTextFromWidgetToContent(data); return data; }); } return ''; }; async function getData( prompt: string, dialogMessages: IChatMessage[], ): Promise<string> { let outputMessage = ''; // your logic to use openai return outputMessage; } return { textToContent: textFromWidgetToContent, renderWidget, textToWidget, }; } ``` To set up your own realization you should use the property `AITranslate` in`QuickBloxUIKitDesktopLayout` or in `MessageView` components like this: Let's modify the QuickBloxUIKitDesktopLayout component from ["Send your first message" page](/ui-kits/react-uikit-send-your-first-message#step-4-updating-the-project-structure-according-to-your-needs) : ```JSX JSX theme={null} import React from 'react'; import * as QB from "quickblox/quickblox"; import { LoginData, QuickBloxUIKitProvider, qbDataContext, RemoteDataSource, useQBConnection, QuickBloxUIKitDesktopLayout, UseDefaultAITranslateWidget } from 'quickblox-react-ui-kit'; function App() { const currentUser: LoginData = { userName: 'YOUR_REGISTRED_USER_NAME', password: 'YOUR_REGISTRED_USER_PASSWORD', }; const apiKey = 'sk-9aXs...ZFqhU'; // use your Api Key const openAIConfiguration: Configuration = new Configuration({ apiKey, }); const openAIApi: OpenAIApi = new OpenAIApi(openAIConfiguration); defaultAITranslateWidget = UseDefaultAITranslateWidget({ ...QBConfig.configAIApi.AITranslateWidgetConfig.proxyConfig, sessionToken: token, }); return ( <QuickBloxUIKitProvider maxFileSize={100 * 1000000} accountData={{ ...QBConfig.credentials }} qbConfig={{ ...QBConfig }} loginData={{ login: currentUser.login, password: currentUser.password, }} > <div className="App"> <QuickBloxUIKitDesktopLayout theme={new DefaultTheme()} AITranslate={{ enabled: true, default: false, AIWidget: defaultAITranslateWidget, }} /> </div> </QuickBloxUIKitProvider> ); } export default App; ``` in MessageView component se detail information on this [page](/ui-kits/react-uikit-screen-modules#group-and-private-chats-screen) : ```JSX JSX theme={null} <DesktopLayout theme={theme} dialogsView={ <DialogsComponent // ... /> } dialogMessagesView={ <MessagesView subHeaderContent={<CompanyLogo/>} upHeaderContent={<CompanyLogo/>} dialogsViewModel={dialogsViewModel} onDialogInformationHandler={informationOpenHandler} theme={theme} AITranslate={defaultAITranslateWidget} /> } dialogInfoView={ <DialogInformation // ... /> } /> ``` <Note> You can compare the configuration of the QuickBloxUIKitDesktopLayout component with the AI Widget to the default view of the QuickBloxUIKitDesktopLayout component from the ["Send your first message" page](/ui-kits/react-uikit-send-your-first-message#step-4-updating-the-project-structure-according-to-your-needs) . </Note> ### How to use the feature as a standalone library You can use logic of the React hook features separately from our UI kit is possible by connecting the library as described below. The library qb-ai-translate contains QBAITranslate object wich provide all nececry features. **install standalone library** QBAITranslate can be installed using Node Package Manager. To include it in your JS based project, follow these command: ```PowerShell PowerShell theme={null} npm install qb-ai-translate ``` **add to own project** To use QBAITranslate in your project, follow these steps: 1. Import the QBAIAnswerAssistant module: ```JavaScript JavaScript theme={null} import { ChatMessage } from 'qb-ai-core'; import { AITranslateSettings, QBAITranslate } from 'qb-ai-translate'; ``` 1. Create an array of ChatMessage\` objects representing the chat history: ```JavaScript JavaScript theme={null} let prompt = "YOUR_TEXT_TO_TRANSLATE"; let messages = [ {role: Role.other, content: "Hello, Bob!"}, {role: Role.me, content: "Hi, Jim!"}, {role: Role.me, content: prompt}, ] ``` or ```JavaScript JavaScript theme={null} let prompt = "YOUR_TEXT_TO_TRANSLATE"; let messages = [ {role: "assistant", content: "Hello, Bob!"}, {role: "user", content: "Hi, Jim!"}, {role: "user", content: prompt}, ] ``` 1. Call the `translate` method to generate translate using an API key: ```JavaScript JavaScript theme={null} const settings: AITranslateSettings = QBAITranslate.createDefaultAITranslateSettings(); settings.apiKey = 'YOUR_OPEN_AI_API_KEY'; settings.organization = 'YOUR_ORGANIZATION';// might be empty settings.model = AIModel.gpt__3_5__turbo; settings.language = 'LANGUAGE'; // in BCP47 format // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages // https://datatracker.ietf.org/doc/html/rfc5646 return QBAITranslate.translate( promt, messages, settings, ); ``` Alternatively, you can use a QuickBlox user token and a proxy URL for more secure communication: ```JavaScript JavaScript theme={null} const settings: AITranslateSettings = QBAITranslate.createDefaultAITranslateSettings(); settings.organization = 'YOUR_ORGANIZATION';// might be empty settings.model = AIModel.gpt__3_5__turbo; settings.token = 'YOUR_QUICKBLOX_USER_SESSION_TOKEN'; settings.language = 'LANGUAGE'; settings.serverPath = `https://your-proxy-server-url`; return QBAITranslate.translate( promt, messages, settings, ); ``` ## AI Rephrase QuickBlox provides answer prephrase message functionality that helps users allows you to rephrase the response for an outgoing message in the chosen tone.. <img /> ### How to use in React UIKit Enabling QuickBlox AI Rephrase Message in React UIKit involves just 2 straightforward actions: 1. Set up the `AIRephraseWidgetConfig` in the `configAIApi` segment of the `QBConfig.ts` file to provide your API Key. 2. Optionally, initialize the `AIRephrase` property within the` QuickBloxUIKitDesktopLayout` (or `MessageView`) component for customizing the appearance or logic of the AI Rephrase component. There are two ways to use the QuickBlox AI Rephrase: 1. **Direct**: Access Open AI directly from your client code. The `AIRephrase` component use a raw Open AI token. This is suitable for development or demos, but it's not the recommended approach for production due to security considerations. 2. **Proxy**: Access Open AI through a proxy server. The `AIRephrase` component use QuickBlox user session token with a proxy server. This is the recommended method for production environments, ensuring a more secure and controlled setup. **Directly using configuration** To utilize the QuickBlox **AI Rephrase Message React hook** to communicate directly with the OpenAI API , you should configure the **AIRephraseWidgetConfig** section in the configuration file, as shown in the code snippet provided below. The token should be stored in the `apiKey` property in the `configAIApi` section in the `QBConfig.ts` file. This contains information about configurations for **[all AI features](/ui-kits/react-uikit-ai-features#overview)** . AFter that the`AIRephrase` component will communicate directly with the OpenAI API. ```TypeScript TypeScript theme={null} export const QBConfig = { credentials: { //.. other section data }, configAIApi: { AIRephraseWidgetConfig: { apiKey: 'sk-k2...LfK', // use your Api Key useDefault: true, defaultTone: 'Professional', Tones: [ { name: 'Professional Tone', description: 'This would edit messages to sound more formal, using technical vocabulary, clear sentence structures, and maintaining a respectful tone. It would avoid colloquial language and ensure appropriate salutations and sign-offs', iconEmoji: '👔', }, { name: 'Friendly Tone', description: 'This would adjust messages to reflect a casual, friendly tone. It would incorporate casual language, use emoticons, exclamation points, and other informalities to make the message seem more friendly and approachable.', iconEmoji: '🤝', }, { name: 'Encouraging Tone', description: 'This tone would be useful for motivation and encouragement. It would include positive words, affirmations, and express support and belief in the recipient.', iconEmoji: '💪', }, { name: 'Empathetic Tone', description: 'This tone would be utilized to display understanding and empathy. It would involve softer language, acknowledging feelings, and demonstrating compassion and support.', iconEmoji: '🤲', }, { name: 'Neutral Tone', description: 'For times when you want to maintain an even, unbiased, and objective tone. It would avoid extreme language and emotive words, opting for clear, straightforward communication.', iconEmoji: '😐', }, { name: 'Assertive Tone', description: 'This tone is beneficial for making clear points, standing ground, or in negotiations. It uses direct language, is confident, and does not mince words.', iconEmoji: '🔨', }, { name: 'Instructive Tone', description: 'This tone would be useful for tutorials, guides, or other teaching and training materials. It is clear, concise, and walks the reader through steps or processes in a logical manner.', iconEmoji: '📖', }, { name: 'Persuasive Tone', description: 'This tone can be used when trying to convince someone or argue a point. It uses persuasive language, powerful words, and logical reasoning.', iconEmoji: '☝️', }, { name: 'Sarcastic/Ironic Tone', description: 'This tone can make the communication more humorous or show an ironic stance. It is harder to implement as it requires the AI to understand nuanced language and may not always be taken as intended by the reader.', iconEmoji: '😏', }, { name: 'Poetic Tone', description: 'This would add an artistic touch to messages, using figurative language, rhymes, and rhythm to create a more expressive text.', iconEmoji: '🎭', }, ], }, }, appConfig: { //... other section data }, }; ``` | Parameter name | Type | Description | | -------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | apiKey | String | This field should hold your actual API key that you'll receive from the service provider (in this case, OpenAI). This key is used to authenticate your requests to the AI service. | | useDefault | Boolean | This setting seems to indicate whether you want to use default settings for the AI answer assistance widget. If set to true, it implies that the default settings will be applied. | | Tones | array of Tone | Array of objects describing the tones that users can use to rephrase text via the Rephrase component menu. Each tone should be defined as an object implementing the 'Tone' interface: interface Tone `{name: string;description: string;iconEmoji: string;}` | **Proxy using configuration** To utilize the QuickBlox **AI Rephrase Message React hook** to communicate through a proxy server (not directly) with OpenAI API, you should configure the **AIRephraseWidgetConfig** section in the configuration file, as shown in the code snippet provided below. The fields `api`, `servername`, `port`, and `sessionToken` need to be configured within the proxyConfig section for communication with the proxy server. ```TypeScript TypeScript theme={null} export const QBConfig = { credentials: { //... other section data }, configAIApi: { AIRephraseWidgetConfig: { apiKey: '', // must be empty useDefault: true, proxyConfig: { api: 'v1/chat/completions', servername: 'https://myproxy.com', port: '', sessionToken: '', }, }, }, appConfig: { //... other section data }, }; ``` | Parameter name | Type | Description | | -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ | | api | String | The specific API endpoint for AI completions. In default, it's set to v1/chat/completions. You should provide your url. | | serverName | String | The base URL for API requests. In default, it's set to '[https://api.openai.com/'](https://api.openai.com/'). You should provide your url. | | sessionToken | String | Your should provide the QuickBlox user session token by default if you are using our proxy server. | | port | String | Default should be empty. | <Tip> We recommend using our proxy server, which you can access through this [link](https://github.com/QuickBlox/qb-ai-assistant-proxy-server). Please ensure that you are using the latest release. </Tip> **AI Rephrase customization** You have the ability to control AI Rephrase Message from code as follows: set it to enabled/disabled, customize the appearance of the component icon, and modify its operational logic. To enable or disable Answer Assist use the following code: ```JSX JSX theme={null} <QuickBloxUIKitDesktopLayout theme={new CustomTheme()} AIRephrase={{ enabled: true, default: true }} /> ``` The `QuickBloxUIKitDesktopLayout` is a layout component located within the provider section. We explored an example of its usage in our UIKit documentation, specifically in the [Send your First Message](/ui-kits/react-uikit-send-your-first-message#step-5-configure-quickbloxuikitprovider) section. To customize the appearance of the component icon, you should create your own React custom hook that must return an object of the AIMessageWidget interface and update the code within the textToWidget method. For modifying its operational logic, you should create your own React custom hook that must return an object of the AIMessageWidget interface and update the code within the textToWidget method. The interface `AIMessageWidget` has next structure: | Parameter name | Type | Description | | -------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | renderWidget | Object function | The renderWidget provides the default appearance of the widget. This is a method that returns a JSX element (React component). It's responsible for rendering the widget, and depending on the presence of errors, it might display an error icon or a support icon. You should change it to implement your own icon. | | textToWidget | Object function | The texToWidget provides the default logic of the widget. This is a function that takes a string (text) and context (in this case, an array of chat messages) but doesn't return anything (void). This function is used to pass text into the widget. You should change it to implement your logic. | | textToContent | String | The textToContent provides the output of the default algorithm implementation. This is a string field representing content for the text widget. It's likely to be used to display the response obtained from AI in the text widget. | Example code of custom hook: ```TypeScript TypeScript theme={null} interface MessageWidgetProps { servername: string; api: string; port: string; sessionToken: string; } export default function UseDefaultAIRephraseMessageWidget({ servername, api, port, sessionToken, }: MessageWidgetProps): AIMessageWidget { const [errorMessage, setErrorMessage] = useState<string>(''); const [textFromWidgetToContent, setTextFromWidgetToContent] = useState(''); const renderWidget = (): JSX.Element => { if (errorMessage && errorMessage.length > 0) { const errorsDescriptions: | { title: string; action: () => void }[] | undefined = []; return ( <ErrorMessageIcon errorMessageText={errorMessage} errorsDescriptions={errorsDescriptions} /> ); } // you can customize your own Widget Icon markup return <AIWidgetIcon applyZoom color="green"/>; }; const textToWidget = async ( textToSend: string, context: IChatMessage[], additionalSettings?: { [key: string]: any }, ): Promise<string> => { if (textToSend && textToSend.length > 0) { let prompt = "Your prompt text with textToSend data"; const { yourAdditionalParam } = additionalSettings || {}; // your logic // eslint-disable-next-line no-return-await return await getData(prompt, context).then((data) => { setTextFromWidgetToContent(data); return data; }); } // return ''; }; async function getData( prompt: string, dialogMessages: IChatMessage[], ): Promise<string> { let outputMessage = ''; // your logic to use openai return outputMessage; } return { textToContent: textFromWidgetToContent, renderWidget, textToWidget, }; } ``` To set up your own realization you should use the property `AIRephrase` in`QuickBloxUIKitDesktopLayout` or in `MessageView` components like this: Let's modify the QuickBloxUIKitDesktopLayout component from ["Send your first message" page](/ui-kits/react-uikit-send-your-first-message#step-4-updating-the-project-structure-according-to-your-needs) : ```JSX JSX theme={null} import React from 'react'; import * as QB from "quickblox/quickblox"; import { LoginData, QuickBloxUIKitProvider, qbDataContext, RemoteDataSource, useQBConnection, QuickBloxUIKitDesktopLayout, UseDefaultAIRephraseMessageWidget, } from 'quickblox-react-ui-kit'; function App() { const currentUser: LoginData = { userName: 'YOUR_REGISTRED_USER_NAME', password: 'YOUR_REGISTRED_USER_PASSWORD', }; const apiKey = 'sk-9aXs...ZFqhU'; // use your Api Key const openAIConfiguration: Configuration = new Configuration({ apiKey, }); const openAIApi: OpenAIApi = new OpenAIApi(openAIConfiguration); const defaultAIRephraseMessageWidget = UseDefaultAIRephraseMessageWidget({ ...QBConfig.configAIApi.AITranslateWidgetConfig.proxyConfig, sessionToken: token, }); return ( <QuickBloxUIKitProvider maxFileSize={100 * 1000000} accountData={{ ...QBConfig.credentials }} qbConfig={{ ...QBConfig }} loginData={{ login: currentUser.login, password: currentUser.password, }} > <div className="App"> <QuickBloxUIKitDesktopLayout theme={new DefaultTheme()} AIRephrase={{ enabled: true, default: false, AIWidget: defaultAIRephraseMessageWidget, }} /> </div> </QuickBloxUIKitProvider> ); } export default App; ``` in MessageView component se detail information on this [page](/ui-kits/react-uikit-screen-modules#group-and-private-chats-screen) : ```JSX JSX theme={null} <DesktopLayout theme={theme} dialogsView={ <DialogsComponent // ... /> } dialogMessagesView={ <MessagesView subHeaderContent={<CompanyLogo/>} upHeaderContent={<CompanyLogo/>} dialogsViewModel={dialogsViewModel} onDialogInformationHandler={informationOpenHandler} theme={theme} AIRephrase={defaultAIRephraseMessageWidget} /> } dialogInfoView={ <DialogInformation // ... /> } /> ``` <Tip> You can compare the configuration of the QuickBloxUIKitDesktopLayout component with the AI Widget to the default view of the QuickBloxUIKitDesktopLayout component from the ["Send your first message" page](/ui-kits/react-uikit-send-your-first-message#step-4-updating-the-project-structure-according-to-your-needs) . </Tip> ### How to use the feature as a standalone library You can use logic of the React hook features separately from our UI kit is possible by connecting the library as described below. The library qb-ai-rephrase contains QBAIRephrase object wich provide all nececry features. **Install standalone library** QBAIRephrase can be installed using Node Package Manager. To include it in your JS based project, follow these command: ```PowerShell PowerShell theme={null} npm install qb-ai-rephrase ``` **Add to own project** To use QBAIRephrase in your project, follow these steps: 1. Import the QBAIRephrase module: ```JavaScript JavaScript theme={null} import { ChatMessage } from 'qb-ai-core'; import { AIRephraseSettings, QBAIRephrase } from 'qb-ai-rephrase'; ``` 1. Create an array of `ChatMessage` objects representing the chat history: ```JavaScript JavaScript theme={null} let prompt = "YOUR_TEXT_TO_REPHRASE"; let messages = [ {role: Role.other, content: "Hello, Bob!"}, {role: Role.me, content: "Hi, Jim!"}, {role: Role.me, content: prompt}, ] ``` or ```JavaScript JavaScript theme={null} let prompt = "YOUR_TEXT_TO_REPHRASE"; let messages = [ {role: "assistant", content: "Hello, Bob!"}, {role: "user", content: "Hi, Jim!"}, {role: "user", content: prompt}, ] ``` 1. Call the `rephrase` method to generate translate using an API key: ```JavaScript JavaScript theme={null} const settings: AIRephraseSettings = QBAIRephrase.createDefaultAIRephraseSettings(); settings.apiKey = 'YOUR_OPEN_AI_API_KEY'; settings.organization = 'YOUR_ORGANIZATION';// might be empty settings.model = AIModel.gpt__3_5__turbo; settings.tone = { name : 'YOUR_TONE_NAME', description: 'YOUR_TONE_DESCRIPTION', // might be empty iconEmoji: 'YOUR_EMOJI'}; // might be empty return QBAIRephrase.rephrase( promt, messages, settings, ); ``` Alternatively, you can use a QuickBlox user token and a proxy URL for more secure communication: ```JavaScript JavaScript theme={null} const settings: AIRephraseSettings = QBAIRephrase.createDefaultAIRephraseSettings(); settings.organization = 'YOUR_ORGANIZATION';// might be empty settings.model = AIModel.gpt__3_5__turbo; settings.tone = { name : 'YOUR_TONE_NAME', description: 'YOUR_TONE_DESCRIPTION', // might be empty iconEmoji: 'YOUR_EMOJI'}; // might be empty settings.token = 'YOUR_QUICKBLOX_USER_SESSION_TOKEN'; settings.serverPath = `https://your-proxy-server-url`; return QBAIRephrase.rephrase( promt, messages, settings, ); ``` # Core components Source: https://docs.quickblox.com/ui-kits/react-uikit-core-components Core components play a crucial role in UIKit for React. ## Overview UIKit includes two main components: **QuickBloxUIKitProvider** and **useUIKitDataContext**. They are responsible for handling and storing all the data needed for the client application to function. Working together, they create and implement views. While they don't provide a user interface themselves, there are specific components dedicated to building the interface. Currently, the following interface components are included: **QuickBloxUIKitDesktopLayout** and **DesktopLayout**. QuickBloxUIKitDesktopLayout arranges user components in three columns, optimized for desktop browsers. DesktopLayout manages the routing logic when changing active dialogs. These components serve as wrappers for all interface components to be discussed in the next section [Screen Module](/ui-kits/react-uikit-screen-modules). <Note> New Updates: Mobile Browser Components Coming Soon to UIKit Components similar to QuickBloxUIKitDesktopLayout and DesktopLayout, designed for displaying the UIKit on mobile browsers, will be added in future updates. </Note> ## QuickBloxUIKitProvider The QuickBloxUIKitProvider is the most important component in UIKit for React because it's the context provider that passes data to the child components. The React Context API is used to easily pass down data through components. By using the useUIKitDataContext() component, you can integrate QuickBlox SDK for React into any of the components under QuickBloxUIKitProvider. The following table shows a list of properties of the QuickBloxUIKitProvider component. List of properties of QuickBloxUIKitProvider: | Property name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | maxFileSize | Number | The maxFileSize parameter is responsible for the size of files that can be used as attachments in messages. This value depends on the type of your account and affects the duration of the audio message. By default, it does not exceed 10 MB. | | accountData | Object | Сontains information about SDK initialization settings, such as appId, accountKey, authKey, authSecret, and sessionToken. (Usually matching the credentials section in the settings file)The appId and accountKey parameters are mandatory.While the authKey and authSecret parameters are filled only when storing data in the app. If the initialization occurs on the server using a sessionToken, the authKey and authSecret parameters remain empty. It's important to note that the accountData structure always includes all fields but fills only the necessary ones./*type AccountData = `{appId: number;accountKey: string;authSecret?: string;authKey?: string;sessionToken?: string;};`* / | | loginData | Object | Not required field. It contains data about the logged-in user (login and password, which can be empty). If this structure is used and has changed, it triggers the relogin flow. | Приведем пример кода настройки провайдера [link](/ui-kits/react-uikit-send-your-first-message#step-4-updating-the-project-structure-according-to-your-needs) ```Typescript TypeScript theme={null} <QuickBloxUIKitProvider maxFileSize={100 * 1000000} accountData={{ ...QBConfig.credentials }} loginData={{ login: currentUser.login, password: currentUser.password, }} > // ... </QuickBloxUIKitProvider> ``` ## useUIKitDataContext By using the custom hook useUIKitDataContext(), you can integrate QuickBlox SDK for React into any of the components that are inside QuickBloxUIKitProvider in the component hierarchy. This hook is responsible for storing all the data needed for the client application and encapsulates parts of the MVVM pattern such as Source and Repositories. This hook returns the QBDataContextType context variable, whose fields, methods and event are described in the table below. QBDataContextType fields: | Property name | Type | Description | | ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | storage | QBDataStorage | This property holds an object of type QBDataStorage, representing some data storage specific to the QBDataContext.The QBDataStorage structure is part of a project implementing the MVVM pattern and provides abstractions for managing various data sources and interacting with business logic. | | InitParams | InitParams | This property holds an object of type InitParams, which likely contains initialization parameters required for the QBDataContext./*export type InitParams = `{maxFileSize: number;accountData: AccountData;qbConfig?: QBConfig;loginData?: LoginData;}`;* / | <Note> Quick Insights into QBDataStorage: An Overview ``` type QBDataStorage = { LOCAL\_DATA\_SOURCE: LocalDataSource; REMOTE\_DATA\_SOURCE: RemoteDataSource; SYNC\_DIALOGS\_USE\_CASE: BaseUseCase<boolean, boolean>; CONNECTION\_REPOSITORY: ConnectionRepository; EVENT\_MESSAGE\_REPOSITORY: EventMessagesRepository; }; ``` **LOCAL\_DATA\_SOURCE:** This field represents the local data source (LocalDataSource) for storing and managing local information in the project. In our case, it is SessionStorage. **REMOTE\_DATA\_SOURCE:** This field represents the remote data source (RemoteDataSource) for handling requests to a remote server or API. In our project, it is a class responsible for handling network requests and fetching data from the server - it acts as a wrapper for all calls to our SDK. **SYNC\_DIALOGS\_USE\_CASE:** This field represents an instance of a class implementing the base use case (BaseUseCase) for synchronizing dialogs or chats. Use case is a part of the MVVM pattern, denoting a specific action that can be performed when interacting with the user interface or other system components. **CONNECTION\_REPOSITORY:** This field represents the repository (ConnectionRepository) for managing the network or internet connection. It is responsible for checking the availability of the internet connection, managing the network state, and handling network interaction errors. **EVENT\_MESSAGE\_REPOSITORY:** This field represents the repository (EventMessagesRepository) for managing messages or events in the system. It is a class that processes and stores information about messages, events, or notifications that should be displayed to the user. </Note> QBDataContextType methods: | method name | description | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | updateQBInitParams | This method allows updating the InitParams property of the QBDataContext with a new value of type InitParams. | | release | This method is intended to perform some cleanup or resource deallocation when it's called. | | authorize | This method is an asynchronous function that takes an object of type AuthorizationData as an argument. It is likely used to handle authorization and authentication processes related to the QBDataContext. | | setSubscribeOnSessionExpiredListener | This method sets a listener for handling a session expiration event. It takes a callback function of type CallBackFunction as an argument | The code below (from our sample ) describe how to use hook useUIKitDataContext() to subscribe on session expired event: ```Typescript TypeScript theme={null} const qbUIKitContext: QBDataContextType = useUIKitDataContext(); /* const authData: AuthorizationData = { userId: 'VALUE_FROM_USER_SESSION', password: 'SESSION_TOKEN', userName: 'LOGIN_USER', sessionToken: 'SESSION_TOKEN', }; */ //... await qbUIKitContext.authorize(authData); qbUIKitContext.setSubscribeOnSessionExpiredListener(() => { console.log('Session has Expired'); }); ``` ## DesktopLayout The DesktopLayout component represents the main layout structure for desktop browsers. It includes three sections and theme. Sections mean columns, each of which contains special components. * The first section, dialogsView, is intended to display the list of dialogs. The DialogsComponent component is responsible for displaying this section. * The second section, dialogMessagesView, allows viewing the list of messages from the selected dialog (chat). The MessagesView component handles the display and logic of this section. * The third section, dialogInfoView, is responsible for showing the settings of a specific chat, such as the list of participants, name, and icon. The DialogInformation component handles the display and logic of this section. Thus, the DesktopLayout component combines these three sections (columns) into a convenient and functional structure for representing and interacting with dialog and chat data in the application. ```Typescript TypeScript theme={null} <DesktopLayout theme={theme} dialogsView={ <DialogsComponent // ... /> } dialogMessagesView={ <MessagesView // ... /> } dialogInfoView={ <DialogInformation // ... /> } /> ``` List of properties of DesktopLayout component: | Property name | Type | Description | | ------------------ | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | theme | UiKitTheme | The custom or default UIKit theme is used to style all the inner components. [link](/ui-kits/react-uikit-customization#theme-colors) | | dialogsView | React Node Element (React.ReactNode) | The components responsible for creating the layout of the dialogs list and handling its functionality. Usually, this is the DialogsComponent component from our UIKit, which serves as the View part of the MVVM pattern. | | dialogMessagesView | React Node Element (React.ReactNode) | The components responsible for creating the layout of the selected dialog's messages list and handling its functionality - what we call the chat. Usually, this is the MessagesView component from our UIKit, which serves as the View part of the MVVM pattern. | | dialogInfoView | React Node Element (React.ReactNode) | The components responsible for creating the layout of the selected dialog's information - changing the name, adding or removing participants from the dialog. Usually, this is the DialogInformation component from our UIKit, which serves as the View part of the MVVM pattern. | ### QuickBloxUIKitDesktopLayout The QuickBloxUIKitDesktopLayout component is a wrapper around DesktopLayout and hides the useEffect hooks. <Tip> Customize Your Layout: Explore QuickBloxUIKitDesktopLayout and DesktopLayout You can find the code for **QuickBloxUIKitDesktopLayout** and **DesktopLayout** in **\*src/Presentation/components/layouts/Desktop**. Pay attention to **DesktopLayout.scss**, where you can make changes to the markup, such as sizing and ordering of columns. [link](https://github.com/QuickBlox/react-ui-kit/tree/main/src/Presentation/components/layouts/Desktop) </Tip> # Customization Source: https://docs.quickblox.com/ui-kits/react-uikit-customization The QuickBlox UIKit for React allows you to create your own unique view of the UIKit. ## Default themes The QuickBlox UIKit for React has 2 built in themes: Dark and Light. ## Color Theme <Frame> <img /> </Frame> Default theme for UIKit is Light. To set theme you need to set value 'dark' to key data-theme in global styles (html\[data-theme="dark”]). For example, you can use pure JavaScript: ```JavaScript JavaScript theme={null} document.documentElement.setAttribute('data-theme', 'dark'); ``` ## Use your own theme There are two options how you can create your own theme: * Customize current theme using css * Create your own theme to customize selected components To customize the current theme you just need to set the new colors in css variables. Or you can create your own theme. To do this you need to create a new class that implements the UiKitTheme interface. To use your own theme using css you need to create \_theme\_colors\_scheme.scss and set colors ```SCSS SCSS theme={null} $background-overlay-light: rgba(19, 29, 40, .80); $background-overlay-dark: rgba(144, 151, 159, .80); $primary-50: #E7EFFF; $primary-100: #C4D7FE; $primary-200: #9CBCFE; $primary-300: #74A1FD; $primary-400: #578CFC; $primary-500: #3978FC; $primary-600: #3370FC; $primary-700: #2C65FB; $primary-800: #245BFB; $primary-900: #1748FA; $primary-a-100: #FFFFFF; $primary-a-200: #F7F9FF; $primary-a-400: #C4CFFF; $primary-a-700: #ABBAFF; $secondary-50: #E4E6E8; $secondary-100: #BCC1C5; $secondary-200: #90979F; $secondary-300: #636D78; $secondary-400: #414E5B; $secondary-500: #202F3E; $secondary-600: #1C2A38; $secondary-700: #182330; $secondary-800: #131D28; $secondary-900: #0B121B; $secondary-a-100: #74A1FD; $secondary-a-200: #3978FC; $secondary-a-400: #245BFB; $secondary-a-700: #0050DC; $system-green-100: #C8F1D6; $system-green-200: #A4E7BB; $system-green-300: #80DDA0; $system-green-400: #64D68B; $system-green-500: #49CF77; $error-100: #FFC4C1; $error-200: #FF9D98; $error-300: #FF766E; $error-400: #FF584F; $error-500: #FF3B30; $information: #FDB0FF; $highlight: #FFFDC1; ``` To use your own new theme colors create \_theme\_dark.scss and \_theme\_light.scss files and set color variable. ```SCSS SCSS theme={null} // _theme_dark.scss: html[data-theme="dark"]{ --color-background-info: #{$primary-500}; --tertiary-elements: #{$background-overlay-dark}; --main-elements: #{$primary-300}; --secondary-elements: #{$primary-a-100}; --input-elements: #{$secondary-200}; --disabled-elements: #{$secondary-300}; --field-border: #{$secondary-200}; --main-text: #{$primary-a-100}; --secondary-text: #{$secondary-200}; --caption: #{$secondary-100}; --main-background: #{$secondary-500}; --secondary-background: #{$secondary-800}; --incoming-background: #{$secondary-400}; --outgoing-background: #{$primary-500}; --dropdown-background: #{$secondary-400}; --chat-input: #{$secondary-800}; --divider: #{$secondary-400}; --error: #{$error-300}; --hightlight: #{$highlight}; } // _theme_light.scss: :root{ --color-background-info:#{$primary-100}; --tertiary-elements: #{$secondary-300}; --main-elements: #{$primary-500}; --secondary-elements: #{$secondary-500}; --input-elements: #{$secondary-500}; --disabled-elements: #{$secondary-100}; --field-border: #{$secondary-200}; --main-text: #{$secondary-900}; --secondary-text: #{$secondary-300}; --caption: #{$secondary-200}; --main-background: #{$primary-a-100}; --secondary-background: #{$primary-a-100}; --secondary-background-modal: #{$background-overlay-light}; --incoming-background: #{$secondary-50}; --outgoing-background: #{$primary-50}; --dropdown-background: #{$primary-a-100}; --chat-input: #{$primary-a-200}; --divider: #{$primary-50}; --error: #{$error-500}; --hightlight: #{$highlight}; } ``` To create your own theme to customize selected components you need to create a new class that implements the UiKitTheme interface. ```TypeScript TypeScript theme={null} //DefaultTheme implements UiKitTheme export default class CustomTheme extends DefaultTheme { divider = (): string => 'var(--divider)'; mainText = (): string => '#FFFFFF'; fontFamily = (): string => 'Roboto'; /* The DefaultTheme contains other theme methods : caption = (): string => 'var(--caption)'; chatInput = (): string => 'var(--chat-input)'; disabledElements = (): string => 'var(--disabled-elements)'; dropdownBackground = (): string => 'var(--dropdown-background)'; error = (): string => 'var(--error)'; fieldBorder = (): string => 'var(--field-border)'; hightlight = (): string => 'var(--hightlight)'; incomingBackground = (): string => 'var(--incoming-background)'; inputElements = (): string => 'var(--input-elements)'; mainBackground = (): string => 'var(--main-background)'; mainElements = (): string => 'var(--main-elements)'; outgoingBackground = (): string => 'var(--outgoing-background)'; secondaryBackground = (): string => 'var(--secondary-background)'; secondaryElements = (): string => 'var(--secondary-elements)'; secondaryText = (): string => 'var(--secondary-text)'; */ } ``` and specify selected components: * desktop layout container ```TypeScript TypeScript theme={null} <QuickBloxUIKitDesktopLayout theme={new CustomTheme()}> ``` * header of dialogs ```TypeScript TypeScript theme={null} <HeaderDialogs title="Dialog 2" theme={new CustomTheme()}/> ``` * item of dialogs ```TypeScript TypeScript theme={null} <PreviewDialog theme={{ selected: true, muted: true, colorTheme={new CustomTheme()} }} title="Dialog with states" unreadMessageCount={9} message_date_time_sent="5 min ago" /> ``` More information about how to implement your own theme you can find in our samples. <Tip> Quick start using our React UIKit look [React UIKit Sample](https://github.com/QuickBlox/quickblox-javascript-sdk/tree/gh-pages/samples/react-chat). This sample implements authorization functionality and provides an example of color theme customization. The sample code is available by this [link](https://github.com/QuickBlox/quickblox-javascript-sdk/tree/gh-pages/samples/react-chat). How to set up and run a sample, See blog [How to create a React Chat Application in just a Few Steps](https://quickblox.com/blog/how-to-create-a-react-chat-application-with-quickblox-uikit/). </Tip> # Features Source: https://docs.quickblox.com/ui-kits/react-uikit-features The **QuickBlox React Chat UIKit** is a set of ready-made components and tools for developing functional and powerful chat features in React applications. ## Overview It provides developers with a convenient way to integrate chat functionality into their projects, enabling them to quickly create interactive and responsive chat interfaces. Some **key features and capabilities** of QuickBlox React Chat UIKit include: **Ready-made components for Chat**: UIKit offers pre-built components for displaying and managing various aspects of a chat, such as contact lists, headers, messages, user profile information, and more. These components can be easily customized and adapted to meet the project's requirements. **Customization**: Developers can customize the appearance and styling of the chat using various parameters and styles. This allows for creating unique and visually consistent chat interfaces that align with the application's design. **Sending and receiving messages**: UIKit provides an easy way to send and receive messages between users. Developers can use ready-made methods and events to handle real-time message sending and receiving. **Contact management**: The library allows for managing the contact list, adding and removing users from the contact list, as well as viewing user profile information. **Various functional capabilities**: UIKit offers additional functional capabilities such as file sharing, group chats, message history viewing, and more, making the chat interface more comprehensive and feature-rich. QuickBlox React Chat UIKit provides a convenient and efficient way to integrate chat into React applications, reducing development time and simplifying tasks related to chat functionality implementation. It can be a useful tool for developers who want to quickly add chat to their projects without the need to build everything from scratch. **Supported features by dialog types** | Group dialog | Private dialog | | | ---------------------- | -------------- | - | | `File sharing` | ✓ | ✓ | | `Voice message` | ✓ | ✓ | | `Typing indicator` | ✓ | ✓ | | `Delivery receipt` | ✓ | ✓ | | `Read receipt` | ✓ | ✓ | | `Unread message count` | ✓ | ✓ | | `Highlight URLs` | ✓ | ✓ | <Note> Adding a Public dialog is planned in the next releases. </Note> ### Dialog types QuickBlox UIKit provides support for three types of dialogs: public dialog, group dialog, and private dialog. **Public dialog** A public dialog is an open chat that is shown to everyone in the app and anyone can join it. Public dialogs can host more participants compared to group dialogs, but does not have a list of participants and their online statuses within dialog. **Group dialog** A group dialog is a chat between closed group of people. New participant can be added either by dialog owner or any other participant of the group dialog. **Private dialog** A private dialog is a direct chat between two people, it can't be extended to host more people. ## File sharing QuickBlox allows users to easily send different types of files, like images, audio files, or videos, during a chat session with the help of the file sharing feature. The file sharing icon appears on the UI element for typing messages. <Frame> <img alt="File Sharing" /> </Frame> ### Customize the UI for file sharing You can customize the UI for file sharing using icon resource. **Icon resource** Icon resources are stored in **src/Presentation/components/UI/svgs** directory, all icons are vectors (svg). The following table shows customizable file sharing icon. <Note> The default icon color uses sharing icons (like others). It can be changed in the IconTheme property. To set the icon color, you should use the methods 'inputElements()' or 'mainElements()' from the UiKitTheme interface or configure it in the Theme colors (see [Theme colors](/ui-kits/react-uikit-customization#theme-colors)). </Note> <table> <thead> <tr> <th>Id</th> <th>Component name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>Attachment</td> <td>Media/Attachment</td> <td> <img alt="attachment" /> </td> <td>An icon used to select and add files.</td> </tr> <tr> <td>Play</td> <td>Toogle/ImagePlay</td> <td> <img alt="video placeholder" /> </td> <td>An icon for video placeholder.</td> </tr> <tr> <td>GifFile</td> <td>Media/GifFile</td> <td> <img alt="gif placeholder" /> </td> <td>An icon for gif placeholder.</td> </tr> <tr> <td>Image</td> <td>Media/ImageEmpty</td> <td> <img alt="image placeholder" /> </td> <td>An icon for image placeholder.</td> </tr> <tr> <td>File</td> <td>Media/ImageFile</td> <td> <img alt="file" /> </td> <td>An icon for file.</td> </tr> </tbody> </table> ## Voice message Voice messages are another way for people to communicate without typing text. Through voice messages, you can express yourself better, using tone and intention. Additionally, recording a voice message is quicker than writing a long text, making it more efficient for the sender. The recipient can listen to the message at their convenience, even while doing other tasks. To send a voice message in a chat, you simply need to press and hold the microphone icon. Then, the message will appear in the chat, and the recipient can play it back. <Frame> <img alt="Voice Message" /> </Frame> ### Send a voice message To send a voice message, users need to record the message first. By default, the voice message icon is visible to the right of the input field. While the icon is pressed, the microphone appears and records the sound from the microphone. The voice message icon only appears when the text input field is empty. When the icon is not pressed, the voice message is sent automatically. ### Customize the UI for voice message recorder You can customize the user interface for the voice message recording device using the icon resource. **Icon resource** The icon resources are stored in the **src/Presentation/components/UI/svgs** folder, and all icons are in vector format (svg). The following table shows the customizable icon for file access. <table> <thead> <tr> <th>Id</th> <th>Component Name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>MicOn</td> <td>Toogle/MicOn</td> <td> <img alt="send voice" /> </td> <td>An icon for send voice.</td> </tr> <tr> <td>Record</td> <td>Toogle/Record</td> <td> <img alt="recorder" /> </td> <td>An icon for recorder.</td> </tr> </tbody> </table> ### Customize the UI for voice messages Using the icon resource, it is possible to tailor the user interface for voice messages. <table> <thead> <tr> <th>Icon name</th> <th>Component Name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>Play</td> <td>Toogle/ImagePlay</td> <td> <img alt="audio" /> </td> <td>An icon for audio.</td> </tr> <tr> <td>AudioAttachment</td> <td>AudioAttachmentComponent</td> <td> <img alt="audio background" /> </td> <td>An icon for audio message background.</td> </tr> </tbody> </table> ## Forwarding Messages and Replying Modern messengers and chat platforms offer various functional features, among the most useful of which are message forwarding and reply options. Let's take a closer look at them. <img /> ### Forwarding Messages: Forwarding is the capability to transfer messages from one chat participant to another, similar to email forwarding. It enables efficient exchange and discussion of information within the context of chats and messengers. <Frame> <img alt="Forward dialog: select dialogs" /> </Frame> Once you've selected the dialogue to which you want to forward the message, the forwarded message will be displayed in the selected dialogue like this. <Frame> <img alt="Forwarded messages in dialog" /> </Frame> ### Replying to Messages: Replying is the ability to respond to a specific message, establishing a connection between the reply and the original message. It enhances the structure of communication in a chat, making conversations more clear and organized. <Frame> <img alt="Preview message selected to reply" /> </Frame> After choosing the message to which you've decided to reply, your response to the message will be displayed like this. <Frame> <img alt="Replied message" /> </Frame> ### Customize the UI for forwarding and replying You can customize the interface elements related to forwarding and sending, just like individual components are customized, such as message sending elements and dialogue selection elements. You can customize the "Forward message dialog" pop-up window in the `ForwardMessageFlow.scss` file to modify various aspects of the screen. The `ForwardMessagePreview.scss` file with classes allows you to customize the forward/reply preview components. ## Typing indicator The typing indicator is a useful feature that provides users with a visual signal when someone else is typing a message in the chat window. The indicator remains visible until the user sends a message or clears the text. If the user stops typing for more than 10 seconds, the indicator also disappears. <Frame> <img alt="Typing Indicator" /> </Frame> ## Delivery receipt The delivery notification is a feature that informs the user about the successful delivery of their messages to other users in the chat window. When a message reaches the server, it is marked as sent, and a single checkmark icon appears next to the message's timestamp. Once the message reaches the recipient's device, it is marked as delivered. If the sender's message has been delivered to any recipient in the chat, a double checkmark icon appears next to the message's timestamp. The delivery notification is only visible to the message sender. <Frame> <img alt="Delivery Receipt" /> </Frame> ### Customize the UI for delivery receipt You can customize the user interface for delivery notifications using the icon resource. **Icon resource** The icon resources are stored in the **src/Presentation/components/UI/svgs** folder, and all icons are in vector format (svg). The following table shows the customizable icon for file access. <table> <thead> <tr> <th>Id</th> <th>Component Name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>Sent</td> <td>Status/Sent</td> <td> <img alt="sent status" /> </td> <td>An icon for sent status.</td> </tr> <tr> <td>ViewedDelivered</td> <td>Status/ViewedDelivered</td> <td> <img alt="delivered status" /> </td> <td>An icon for delivered status.</td> </tr> </tbody> </table> <Note> The default delivery receipt color uses the **tertiary elements color**, it can be changed in the theme with `setTertiaryElementsColor()` (see [Theme colors](/ui-kits/android-uikit-customization#theme-colors)) </Note> ## Read receipt The read receipt feature allows users to determine whether their messages have been read by others in the dialog. When any recipient in the dialog have read the sender's message, a colored double-tick icon is displayed next to the message's timestamp. The read receipts are only visible to the sender of the message. It appears on the `MessagesComponent`. <img alt="" /> ### Customize the UI for read receipt You can customize the UI for read receipt using icon resource. **Icon resource** Icon resources are stored in **drawable** directory, all icons are vectors (xml). The following table shows customizable file sharing icon. <table> <thead> <tr> <th>Id</th> <th>Component Name</th> <th>Image</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>ViewedDelivered</td> <td>Status/ViewedDelivered</td> <td> <img alt="read status" /> </td> <td>An icon for read status.</td> </tr> </tbody> </table> <Note> The default read receipt color uses the **main elements color**, it can be changed in the theme with `setMainElementsColor()` (see [Theme colors](/ui-kits/android-uikit-customization#theme-colors)) </Note> ## Unread message count Unread message count is a feature that informs users of the number of messages they haven’t read in each dialog. The number of unread messages per dialog is displayed below the timestamp. If the number of messages is more than 100, it will be displayed as `99+`. The unread message count appears on the `DialogsComponent`. <Frame> <img alt="Unread Message Count" /> </Frame> ## Highlight URLs The highlighting URLs is a feature that refers to displaying clickable links in a chat message, where the URL is visually distinguished and can be interacted with the user. <Frame> <img alt="Highlight URLs" /> </Frame> # Overview Source: https://docs.quickblox.com/ui-kits/react-uikit-overview The main goal of the QuickBlox UIKit for React is to offer developers a streamlined and efficient way to implement chat functionality within their React Web applications." The **QuickBlox UIKit for React** is a comprehensive user interface kit specifically designed for building chat applications. It provides a collection of pre-built components, modules, and utilities that simplify the process of creating chat applications. The main goal of the **QuickBlox UIKit for React** is to offer developers a streamlined and efficient way to implement chat functionality within their React applications. The **QuickBlox UIKit for React** offers modules that encapsulate complex chat functionalities, such as dialogs and chat management and real-time updates. These modules provide a simplified interface for integrating chat features into applications without the need for extensive knowledge of the underlying protocols or server-side infrastructure. <Frame> <img alt="Web Documentation" /> </Frame> ## Features QuickBlox UIKit for React provides the following functionality: * List of dialogs * Create dialog(Private or Group) * Dialog screen * Send text, image, video, audio, file messages * Dialog info screen * List, invite, remove members ## Source code You can find the QuickBlox UIKit for React source code [here](https://github.com/QuickBlox/react-ui-kit) ## Sample <Tip> Quick start using our React UIKit look [React UIKit Sample](https://github.com/QuickBlox/quickblox-javascript-sdk/tree/gh-pages/samples/react-chat-ui-kit-demo-app). This sample implements authorization functionality and provides an example of color theme customization. The sample code is available by this [link](https://github.com/QuickBlox/quickblox-javascript-sdk/tree/gh-pages/samples/react-chat-ui-kit-demo-app). How to set up and run a sample, See blog [How to create a React Chat Application in just a Few Steps](https://quickblox.com/blog/how-to-create-a-react-chat-application-with-quickblox-uikit/). </Tip> # Screen modules Source: https://docs.quickblox.com/ui-kits/react-uikit-screen-modules QuickBlox React Chat UIKit provides customizable screen modules and essential chat functions such as a list of dialogs, dialog creation, in-dialog chat, and dialog editing out of the box. ## Overview The screen modules consist of components with component settings for each of them, as well as the ViewModel model that provides the functionality of the screens. Each screen module has customizable user interface components. By following the steps outlined in the [Get Start](/ui-kits/react-uikit-send-your-first-message) section, you will be able to display QuickBlox React Chat UIKit. <Frame> <img /> </Frame> The QuickBlox React Chat UIKit screen is divided into three main screen modules: * Dialogs List * Message History for each dialog * Dialog Information Refer to the table below to see the screen modules we provide and the components that each screen module consists of. <table> <thead> <tr> <th>Screen Module</th> <th>ViewModel</th> <th>Components</th> </tr> </thead> <tbody> <tr> <td>Dialogs</td> <td>DialogsViewModel</td> <td>HeaderDialogsPreviewDialog</td> </tr> <tr> <td>MessageView</td> <td>DialogsViewModel, MessageViewModel</td> <td>HeaderMessages, AudioAttachmentComponentProps, HighLightLink, ImageAttachmentComponent, VideoAttachmentComponent, VoiceRecordingProgress</td> </tr> <tr> <td>DialogInformation</td> <td>DialogsViewModel, UserViewModel</td> <td>MembersList, InviteMembers, and UsersList</td> </tr> </tbody> </table> ## Dialogs list screen The dialog list displays a complete list of group and private dialogs that the current user is a member of. <Frame> <img /> </Frame> ### Screen structure The screen with the list of dialogs is made up of two components: the header and the dialogs section. **Header** <img /> The header component includes the title for the dialog list and default buttons in the top right corner, which allow you to create dialogs and filter them. The view of the header component is created and configured in the `HeaderDialogsProps`. **Dialog list** <img /> The dialogs component displays a list of all private and group dialogs that the current user is part of. The list of dialogs is generated in the **Dialogs** component based on data from the **ViewModel**. To create the view for each dialog list item, you need to use `PreviewDialog`. When the current user clicks on one of the dialog windows in the list, they will be able to enter the chat screen. ### Features * Display a list of dialogs: The Dialog List component presents a visually appealing list of dialogs in the application. <img /> * Select and view dialog details: Users can select a dialog from the list to view more details about the selected dialog. <Frame> <img /> </Frame> * Delete dialogs: The feature allows users to delete dialogs from the list, removing them from their conversation history. <Frame> <img /> </Frame> * Search functionality: Users can search for specific dialogs using the provided search bar. <Frame> <img /> </Frame> * Sync state indication: The sync state is provided to indicate whether the dialog list is currently syncing data with the server. <img /> ### Usage The Dialog List feature in QuickBlox UIKit provides a user interface for displaying and managing a list of dialogs in an application. It allows users to view, select, and perform actions on individual dialogs, such as deleting or leaving a dialog. The "Dialog" component is designed to display a list of dialogs (chats) and consists of two parts: "HeaderDialog" (list header) and "PreviewDialog" (preview of a specific dialog). You can create your own custom component for the dialog header (as well as place your custom components above and below it). This allows you to add your logo to the UIKit interface elements. However, the "PreviewDialog" component can only be customized through themes. <Note> **Note on PreviewDialog:** In the future versions of the UIKit, you will be able to customize and use your own PreviewDialog component. </Note> To use the Dialogs component in the project, you need to add it to the dialogsView section of the DesktopLayout layout component. We wrote about the DesktopLayout component in the Core Components section - here is the [link](/ui-kits/react-uikit-core-components#desktoplayout). Below is a snippet of the DesktopLayout markup that uses the Dialogs component. ```Typescript TypeScript theme={null} <DesktopLayout theme={theme} dialogsView={ <DialogsComponent subHeaderContent={<CompanyDescription/>} upHeaderContent={<CompanyLogo/>} dialogsViewModel={dialogsViewModel} onDialogSelectHandler={selectDialogActions} additionalSettings={{ withoutHeader: false, themeHeader: theme, themePreview: theme, useSubHeader: true, useUpHeader: true, }} /> } dialogMessagesView={ <MessagesView // ... /> } dialogInfoView={ <DialogInformation // ... /> } /> ``` ### Customization Components HeaderDialog and PreviewDialog are sensitive to the theme used. HeaderDialog component - the header of the dialog list, uses Search icon, wich you can find on path **src/Presentation/components/UI/svgs/Icons/Navigation/Search**, and New Chat icon, wich you can find on path **src/Presentation/components/UI/svgs/Icons/Actions/NewChat**. HeaderDialog component uses a set of theme elements defined by the following **methods** in the **UiKitTheme** interface: **divider(), mainBackground(), mainText(), fontFamily(), mainElements()**. The styles specific to this component are defined in the **HeaderDialog.scss** file. PreviewDialog component - the preview of each available dialog, uses **PublicChannel, GroupChat, User, and EditDots** icons. All icons are located in the directory **src/Presentation/components/UI/svgs/Icons**. PreviewDialog component uses a set of theme elements defined by the following **methods** in the theme **UiKitTheme** interface: **mainElements(), mainBackground(), disabledElements(), secondaryText(), inputElements(), mainText(), divider(), chatInput(), fieldBorder()**. The styles specific to this component are defined in the **PreviewDialog.scss** file. The Dialog component has the following list of properties: | Property name | Type | Description | | --------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | header | React.ReactNode | This field allows passing a React element as the content for the main header of the chat dialog list. | | upHeaderContent | React.ReactNode | This field allows passing a React element as the content for the upper header of the chat dialog list. | | subHeaderContent | React.ReactNode | This field allows passing a React element as the content for the additional subheader of the chat dialog list. | | dialogsViewModel | DialogsViewModel | This field corresponds to the dialog view model (DialogsViewModel) responsible for handling data and managing the state of the chat dialog list, following the MVVM pattern. | | onDialogSelectHandler | Object function | This field represents a function (Handler) to handle the event of selecting (highlighting) a dialog from the chat dialog list. It might take an object of type DialogEntity representing the selected dialog. | | additionalSettings | Object | This field represents additional settings (DialogsComponentSettings) for the chat dialog list component that we have already discussed./*type DialogsComponentSettings = `{themeName?: ThemeNames;withoutHeader?: boolean;useSubHeader?: boolean;useUpHeader?: boolean;themeHeader?: UiKitTheme;themePreview?: UiKitTheme;}`;* / | <Note> **Note on DialogsComponentSettings:** * **themeName**?: ThemeNames: This field optionally indicates the theme name (ThemeNames) that can be associated with the components inside the chat dialog list. * **withoutHeader**?: boolean: If this field is set to true, the chat dialog list component will not display the main header. * **useSubHeader**?: boolean: If this field is set to true, the chat dialog list component will use an additional subheader. * **useUpHeader**?: boolean: If this field is set to true, the chat dialog list component will use an upper header. * **themeHeader**?: UiKitTheme: This field optionally indicates the theme (UiKitTheme) for styling the main header of the chat dialog list. * **themePreview**?: UiKitTheme: This field optionally indicates the theme (UiKitTheme) for styling the preview of the chat dialog list. </Note> <img /> ### Usage ## Create New Dialog Screen The dialog list screen shows the complete list of group and private dialogs of which the current user is a member. After following the steps described in the [Get started](/ui-kits/android-uikit-send-your-first-message#get-started) section, you can display and manage the list of dialogs. <Frame> <img /> </Frame> <Note> Coming Soon: Public Dialog Creation in Future UIKit Updates The ability to create public dialogs will be added in future updates of the UIKit. </Note> ## Dialog name screen The dialog name screen is used in the group dialog creation flow and allows you to set a name and photo for a group dialog. <Frame> <img /> </Frame> ### Customization You can customize the "New dialog" pop-up window in the CreateDialog.scss file to modify various aspects of the screen. The UserList.scss file with classes allows you to customize the main list components. ## User list screen The user list screen displays available users that can be selected to create a group or private dialog. <Frame> <img /> </Frame> ## Group and private chats screen A group chat screen allows close interactions between a limited number of users. To join the dialog, one of the members needs to add you to the dialog. A private chat screen allows close interaction between two users. To participate in this type of dialog, you can create a dialog with your opponent or your opponent can create a dialog with you. <Frame> <img /> </Frame> ### Screen structure The chat screen consists of two components: the header and messages. It utilizes two models: DialogsViewModel, which provides information about the current dialog, such as its name, list of participants, and message history, and MessagesViewModel, which describes the collection of messages for a specific dialog. You can access the **DialogsViewModel** using the custom hook **useDialogsViewModel()**. Similarly, you can access the **MessagesViewModel** using the custom hook **useMessagesViewModel()**. The code for these hooks is located in the following files: src/Presentation/Views/Dialogs/useDialogsViewModel.ts and src/Presentation/components/UI/Dialogs/MessagesView/useMessagesViewModel.ts. **Header** The header component contains the name and the photo of the dialog. Also, the header contains a default button in the upper right corner that allows you to open the group chat info screen. **Messages** The message list component shows a list of all messages exchanged in the chat, in chronological order. The list displays both text and file messages, and messages sent by the current user are different from messages sent by other participants in the dialog. **Send Message** The send message component is where the user can either enter a text message or send the message as a file by importing a file, image, or video. ### Usage The MessageView component, designed to display the list of dialogs (chats), consists of three logical parts: the HeaderMessages component (header of the message list), components for displaying messages (VideoAttachmentComponent, AudioAttachmentComponent, ImageAttachmentComponent, HighLightLink), and the component for sending messages. While you cannot create your own custom component for the header of the message list, you can place your own components above and below it. This allows you to add your logo or other custom elements to the UIKit interface. All parts of the MessageView component can be customized using themes. In future versions of the UIKit, you will also be able to set your own components for sending messages and displaying the messages themselves. ### Customization All parts of the MessageView component are sensitive to the theme used. The HeaderMessages component, which serves as the header of the dialog list, uses icons: * To display the type of dialog: GroupChat, UserAvatar, PublicChannel; * To display dialog properties: InformationFill; * To start a video call: Phone; The icons are located in the directory: **src/Presentation/components/UI/svgs/Icons** The HeaderMessages component uses a set of theme elements defined by the following methods in the UiKitTheme interface: divider(), secondaryText(), secondaryBackground(). The specific styles for this component are defined in the HeaderMessages.scss file. Other parts of the MessageView component responsible for displaying messages (VideoAttachmentComponent, AudioAttachmentComponent, ImageAttachmentComponent, HighLightLink) use the icons SentStatusIcon, ViewedDelivered, ImagePlay, AudioFile, ImageEmpty. The icons are located in the directory: **src/Presentation/components/UI/svgs/Icons**. The **MessageView** component uses a set of theme elements defined by the following methods in the **UiKitTheme** interface: mainElements(), mainBackground(), disabledElements(), secondaryText(), inputElements(), mainText(), divider(), chatInput(), fieldBorder(). The specific styles for this component are defined in the **MessageView\.scss** file. Each of the nested components also has its own styles specified in separate files, which customize the parent component's styles. For example, the **VideoAttachmentComponent** component responsible for displaying the appearance of video files is styled in the **VideoAttachmentComponent.scss** file. Similarly, the **AudioAttachmentComponent** component responsible for displaying and playing audio files is styled in the **AudioAttachmentComponent.scss** file. The **ImageAttachmentComponent** component, responsible for displaying the appearance of images, is styled in the **ImageAttachmentComponent.scss** file. The **HighLightLink** component, responsible for displaying the appearance of links in messages, is styled in the **HighLightLink.scss** file. The MessageView component has the following list of properties: | Property name | Type | Description | | -------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | upHeaderContent | React.ReactNode | This field allows passing a React element as the content for the upper header of the chat dialog list. | | subHeaderContent | React.ReactNode | This field allows passing a React element as the content for the additional subheader of the chat dialog list. | | dialogsViewModel | DialogsViewModel | This field represents the dialogs view model (DialogsViewModel) that manages the data and state of the chat messages list. In the context of the MVVM pattern, the view model handles the logic related to data and interaction with the user interface components. | | onDialogInformationHandler | Object function | This field represents the handler function (Handler) for the event of obtaining information about a dialog. It may not take any arguments and performs specific actions when requesting information about a dialog. | | theme | UiKitTheme | This field represents the theme (UiKitTheme) for styling the chat messages list component. In an MVVM pattern-based application, themes are often used to manage the style and appearance of the user interface. | | AIAnswerToMessage | AIWidget | ***In versions 0.2.0*** and later, you can integrate AI functionality based on OpenAI features. This field allows you to specify your own AI Widget for generating quick responses to messages. For more details, please refer to the following AI Feature section of documentation. | <Tip> You can use AI features in UIKit version 0.2.0 or later Please, look at this [section](/ui-kits/react-uikit-ai-features#ai-assist-answer-customization) of our documentation. </Tip> To use the Dialogs component in the project, you need to add it to the dialogsView section of the DesktopLayout layout component. We wrote about the DesktopLayout component in the Core Components section - here is the [link](/ui-kits/react-uikit-core-components#desktoplayout). Below is a snippet of the DesktopLayout markup that uses the Dialogs component. ```Typescript TypeScript theme={null} <DesktopLayout theme={theme} dialogsView={ <DialogsComponent // ... /> } dialogMessagesView={ <MessagesView subHeaderContent={<CompanyLogo/>} upHeaderContent={<CompanyLogo/>} dialogsViewModel={dialogsViewModel} onDialogInformationHandler={informationOpenHandler} theme={theme} /> } dialogInfoView={ <DialogInformation // ... /> } /> ``` ## Dialog information screen The group chat info screen allows configuring the settings of the group dialog. If you owner you can customize settings including dialog name and dialog image, you can also leave the dialog. The **MessageView** component uses two models: **DialogsViewModel** and **UsersListViewModel**. **DialogsViewModel** is used to retrieve information about the current dialog, such as its title, list of participants, and message list. **UsersListViewModel** describes the collection of messages for a specific dialog. You can obtain the **DialogsViewModel** using the custom hook **useDialogsViewModel()** and the **UsersListViewModel** using the custom hook **useUsersListViewModel()**. The code for these hooks is located in the following files: src/Presentation/Views/Dialogs/useDialogsViewModel.ts and src/Presentation/components/UI/Dialogs/DialogInformation/UsersList/useUsersListViewModel.ts. <img alt="" /> ### Screen structure The group chat info screen consists of two components: header, dialog info and dialog members list. We want to draw your attention that the list of participants is only available for group dialogs and is not displayed for private dialogs. <Note> **Note:** The Notification component will be available in the upcoming UIKit updates. </Note> **Header** The header component contains a screen title. If you dialog owner, the header includes a default button "Edit" in the upper right corner that allows you to open the flow for updating the name and image of the dialog. **Dialog info** The dialog info component shows the dialog's cover image and name by default. The view of the channel information is created and customized in the **DialogInfoComponent.sccs** file. **dialog members list** The "Members List" component - **MembersList** - displays the complete list of users who are participants in a group dialog. The display and appearance of member information can be customized in the **MembersList.scss** file. By default, the component shows profile images and names of all participants in the group dialog. We will examine the appearance and functionality of this component separately below. ### Usage The **DialogInformation** component, used to display information about dialogs (chats), consists of several sub-components: **HeaderDialogInformation** (header of the information window), the dialog information itself, which allows changing the dialog name and its icon, and the list of dialog participants. The **MembersList** component displays the participants of the dialog, while the InviteMembers component lists users who can be invited to the dialog. Both of these components are not available for private dialogs. You cannot create your own custom component for the header of the dialog information, but you can place your own component above and below it. This allows you to add your logo to the UIKit elements. However, the DialogInformation component can only be customized through themes. ### Customization All components inside **DialogInformation** are sensitive to the theme used. The HeaderDialogInformation component, which serves as the header of the dialog information, utilizes the following icons: UserAvatar, GroupChat, PublicChannel, NotifyOn, and Search. The icons are located in the directory:**src/Presentation/components/UI/svgs/Icons**. The specific styles for the **DialogInformation** component are defined in the file **DialogInformation.scss**. The components **MembersList** and **InviteMembers**, which are used to preview each available dialog, use the **MainButton** and **SwitchButton** components as buttons. These components are styled in the files **MainButton.scss** and **SwitchButton.scss**. The **MembersList** and **InviteMembers** components use styles defined for them in the files **MembersList.scss** and **InviteMembers.scss**. The **DialogInformation** component has the following list of properties: | Property name | Type | Description | | ------------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | theme | UiKitTheme | This field represents the theme (UiKitTheme) for styling the dialog information component. Themes allow controlling the style and appearance of the user interface in an application built using the MVVM pattern. | | upHeaderContent | React.ReactNode | This field allows passing a React element as the content for the upper header of the chat dialog list. | | subHeaderContent | React.ReactNode | This field allows passing a React element as the content for the additional subheader of the chat dialog list. | | dialogsViewModel | DialogsViewModel | This field represents the dialog view model (DialogsViewModel), which manages data and the state of the dialog list in the chat. In the context of the MVVM pattern, the view model handles logic related to data and interaction with user interface components. | | onCloseDialogInformationHandler | Object function | This field represents a handler function for the event of closing dialog information. This function performs specific actions when the window with dialog information is closed. | | dialog | DialogEntity | This field represents the dialog entity (DialogEntity), which contains information about the selected dialog, such as its ID, name, participants, and other properties that may be relevant for displaying dialog information. | To use the DialogInformation component in the project, you need to add it to the dialogsView section of the DesktopLayout layout component. We wrote about the DesktopLayout component in the Core Components section - here is the [link](/ui-kits/react-uikit-core-components#desktoplayout). Below is a snippet of the DesktopLayout markup that uses the Dialogs component. ```Typescript TypeScript theme={null} <DesktopLayout theme={theme} dialogsView={ <DialogsComponent // ... /> } dialogMessagesView={ <MessagesView // ... /> } dialogInfoView={ <DialogInformation subHeaderContent={<CompanyDescription/>} upHeaderContent={<CompanyLogo/>} dialog={selectedDialog.entity} dialogViewModel={dialogsViewModel} onCloseDialogInformationHandler={informationCloseHandler} /> } /> ``` ### Members screen A members screen is a complete list of users that are members of a group dialog. The dialog member list is created through MembersList components. <Frame> <img /> </Frame> ### Add members screen The add members screen allow invite new members to the group dialog. The dialog member list is created through MembersList or InviteMembers components. All users are listed through the pagination, and members who already belong to the dialog cannot be shown. <Frame> <img /> </Frame> ### Customization The MembersList and InviteMembers components use styles defined specifically for them in the files MembersList.scss and InviteMembers.scss, respectively. # Send your first message Source: https://docs.quickblox.com/ui-kits/react-uikit-send-your-first-message The QuickBlox UIKit for React comprises a collection of pre-assembled UI components that enable effortless creation of an in-app chat equipped with all the necessary messaging functionalities. Our development kit encompasses light and dark themes, colors, and various other features. These components can be personalized to fashion an engaging messaging interface that reflects your brand's distinct identity. The QuickBlox UIKit fully supports both private and group dialogs. To initiate the process of sending a message from the ground up using Java or Kotlin, please refer to the instructions provided in the guide below. ## Requirements The minimum requirements for QuickBlox UIKit for React are: * JS QuickBlox SDK v2.15.5 * React v.18.0 * TypeScript v.4.9.3 ## Before you begin Register a new account following [this link](https://admin.quickblox.com/signup). Type in your email and password to sign in. You can also sign in with your Google or GitHub accounts. Create the app clicking New app button. Configure the app. Type in the information about your organization into corresponding fields and click Add button. Go to Dashboard => YOUR\_APP => Overview section and copy your Application ID, Authorization Key, Authorization Secret, and Account Key . ## Helicopter overview Before you start coding, let's provide a brief introduction and explain the steps for developers. Firstly, you should install our JS SDK and UIKit. Secondly, you need to create a session, log in using the credentials of an already registered user, and connect to the chat. ### Install QuickBlox SDK ```JavaScript JavaScript theme={null} npm install quickblox ``` ### Install QuickBlox UIKit ```JavaScript JavaScript theme={null} npm install quickblox-react-ui-kit ``` ### Init QuickBlox SDK To init QuickBlox SDK you need to pass Application ID, Authorization Key, Authorization Secret, and Account Key to the init() method. ```JavaScript JavaScript theme={null} `var APPLICATION_ID = 41; var AUTH_KEY = "lkjdueksu7392kj"; var AUTH_SECRET = "iiohfdija792hj"; var ACCOUNT_KEY = "sdjfnksnlk2bk1k34kb"; var CONFIG = { debug: true }; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY, CONFIG); ``` <Warning> **Security** It's not recommended to keep your **authKey** and **authSecret** inside an application in production mode, instead of this, the best approach will be to store them on your backend. Instead you can [initialize QuickBlox SDK without Authorization Key and Secret](/sdks/js-setup#install-quickblox-sdk-into-your-app) </Warning> ### Authentication Before sending your first message you need to authenticate users in the QuickBlox system. You can read more about different ways of authentication by [this link](/sdks/js-authentication). In our example we show how to authenticate user with login and password. ```JavaScript JavaScript theme={null} import * as QB from "quickblox/quickblox"; import { QuickBloxUIKitProvider } from 'quickblox-ui-kit-react'; QB.createSession(function(error, result) { var params = { login: "garry", password: "garry5santos" }; QB.login(params, function(error, result) { if(error){ } else { //Connect to chat and navigate User to the UIKit }); }); ``` ## Get started The entire process consists of several steps: * Creating a React Project with TypeScript * Adding Dependencies to a React Project * Adding QBconfig.ts File to Your React Application * Perhaps updating the project structure according to your needs * Configure QuickBloxUIKitProvider ### Step 1. Creating a React Project with TypeScript and Webpack In this section, we will guide you through creating a new React project using TypeScript and Webpack without using `create-react-app`. This manual setup gives you complete control over the build process, which is useful when integrating advanced SDKs and UIKits. **Initializing the Project** 1. Open your terminal and create a new project directory: ```bash theme={null} mkdir react-chat-ui-kit-init-sample cd react-chat-ui-kit-init-sample npm init -y ``` **Installing Dependencies** Install the required libraries and tools: ```bash theme={null} # Application dependencies npm install react react-dom # TypeScript and types npm install --save-dev typescript @types/react @types/react-dom @types/node # Webpack and its core plugins npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin copy-webpack-plugin # Loaders for styles and scripts npm install --save-dev ts-loader style-loader css-loader sass-loader sass # Babel support npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript # SVG loader npm install --save-dev @svgr/webpack ``` **Creating Configuration Files** Create a `tsconfig.json` in the root folder: ```json theme={null} { "compilerOptions": { "target": "ESNext", "lib": ["DOM", "DOM.Iterable", "ESNext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "ESNext", "moduleResolution": "Node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "react-jsx", "noEmit": true }, "include": ["src"] } ``` Also create a `webpack.config.js` file (see example in the QuickBlox repository for a complete working configuration [webpack.config.js](https://github.com/QuickBlox/quickblox-javascript-sdk/blob/gh-pages/samples/react-chat-ui-kit-init-sample/webpack.config.js)). **Creating the Project Structure** Manually create the following folder and file structure: ``` public/ index.html src/ App.tsx App.css index.tsx QBconfig.ts logo.svg webpack.config.js tsconfig.json package.json ``` **Example of `public/index.html`** ```html theme={null} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>React UIKit Example
``` **Creating the Entry Point** In `src/index.tsx`, add the following code: ```tsx theme={null} import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import './index.css'; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render(); ``` **Adding NPM Scripts** In your `package.json`, update the scripts section to include: ```json theme={null} "scripts": { "start": "webpack serve --mode development", "build": "webpack --mode production" } ``` **Running the Project** Start the development server: ```bash theme={null} npm run start ``` Then open your browser at [https://localhost:3000](https://localhost:3000) to view the running application. You now have a working React + TypeScript + Webpack project ready to integrate the QuickBlox UIKit. Continue to the next section to add the required configuration and SDK logic. ### Step2. Adding Dependencies to a React Project To successfully integrate QuickBlox functionality into your React project, you need to add two main dependencies: `quickblox` and `quickblox-react-ui-kit`. By following the documentation, you can easily add these packages to your project. **Installing Dependencies** 1. Open your command prompt or terminal and navigate to the root folder of your React project. 2. Run the following command to install the `quickblox` package: ```Powershell Powershell theme={null} npm install quickblox --save ``` 3. Then, execute the following command to install the `quickblox-react-ui-kit` package: ```Powershell Powershell theme={null} npm install quickblox-react-ui-kit --save ``` **Importing Dependencies in the Project** 1. Open the file where you want to use QuickBlox functionality, such as App.tsx 2. Add the following lines at the beginning of the file to import the dependencies: Note: use // @ts-ignor We are using // @ts-ignor in TypeScript because QuickBlox SDK doesn't share types. ```JavaScript JavaScript theme={null} // @ts-ignore import * as QB from "quickblox/quickblox"; import 'quickblox-react-ui-kit' ``` **Using QuickBlox in the Project** Now that the dependencies are successfully added to your project, you can utilize QuickBlox functionality within the relevant components and modules of your application. Refer to the QuickBlox and QuickBlox UIKit documentation for more detailed information on the available features and components you can use. ### Step 3. Adding QBconfig.ts File to Your React Application To ensure proper configuration and functionality of the QuickBlox UIKit in your React application, it is essential to add a QBconfig.ts file to the src folder. This file allows you to define the necessary parameters for the QuickBlox UIKit. The QBconfig.ts file contains various configuration settings that determine how the QuickBlox UIKit interacts with the QuickBlox JavaScript SDK and the backend services. These settings include: 1. appId: This parameter represents the unique identifier assigned to your QuickBlox application. It helps establish a connection between the frontend and backend components. 2. authKey and authSecret: These parameters are used for authentication purposes. They ensure secure communication between your application and the QuickBlox backend. 3. accountKey: This parameter identifies your QuickBlox account and provides access to the associated services. 4. apiEndpoint and chatEndpoint: These parameters define the API and chat endpoints provided by QuickBlox. They specify the URLs to which the QuickBlox UIKit will send requests for various functionalities. To illustrate, here is an example of a QBconfig.ts file: ```JavaScript JavaScript theme={null} export const QBConfig = { credentials: { appId: YOUR_APP_ID_FROM_ADMIN_PANEL, accountKey: 'YOUR_ACCOUNT_KEY_FROM_ADMIN_PANEL', authKey: 'YOUR_AUTH_KEY_FROM_ADMIN_PANEL', authSecret: 'YOUR_AUTH_SECRET_FROM_ADMIN_PANEL', sessionToken: '', }, appConfig: { chatProtocol: { Active: 2, }, debug: false, endpoints: { apiEndpoint: 'https://api.quickblox.com', chatEndpoint: 'chat.quickblox.com', }, on: { async sessionExpired(handleResponse: any, retry: any) { console.log(`Test sessionExpired… ${handleResponse} ${retry}`); } }, streamManagement: { Enable: true, }, }, }; ``` In this example, make sure to replace the placeholder values (YOUR\_APP\_ID\_FROM\_ADMIN\_PANEL, YOUR\_ACCOUNT\_KEY\_FROM\_ADMIN\_PANEL, YOUR\_AUTH\_KEY\_FROM\_ADMIN\_PANEL, YOUR\_AUTH\_SECRET\_FROM\_ADMIN\_PANEL) with the actual values obtained from your QuickBlox application. By adding the QBconfig.ts file to your React application, you ensure that the QuickBlox UIKit is properly configured and can interact seamlessly with the QuickBlox backend services. Note: if the SDK was initialized using a session token, use the same sessionToken in the UIKit. If you initialize your SDK using a session token, you need to not only fill the sessionToken field in QBConfig but also fill other properties in the same way as when using the initWithAppId method. See the link [initialize QuickBlox SDK without Authorization Key and Secret](/sdks/js-setup#install-quickblox-sdk-into-your-app) Now we can add dependecies in our code. (Open your App.tsx and add that lines.) ```Typescript TypeScript theme={null} import * as QB from "quickblox/quickblox"; import 'quickblox-react-ui-kit'; import { QBConfig } from './QBconfig'; // this line is new ``` ### Step 4. Updating the project structure according to your needs In order to enhance the organization and maintainability of your project, it is recommended to make changes to the structure of the App.tsx in the src folder. You need to add a constant called "currentUser" inside the App() function in the App.tsx file, which describes your user registered in the admin panel, and configure the QuickBloxUIKitProvider. Additionally, import the necessary entities from `quickblox-react-ui-kit`. As a result, you should have the code below: ```TypeScript TypeScript theme={null} import React from 'react'; import * as QB from "quickblox/quickblox"; import { LoginData, QuickBloxUIKitProvider, qbDataContext, RemoteDataSource, useQBConnection, QuickBloxUIKitDesktopLayout, } from 'quickblox-react-ui-kit'; function App() { const currentUser: LoginData = { userName: 'YOUR_REGISTRED_USER_NAME', password: 'YOUR_REGISTRED_USER_PASSWORD', }; return (
); } export default App; ``` QuickBloxUIKitProvider can accept up to three parameters: * maxFileSize - controls the maximum size of uploaded files. * accountData - information about the application's account data. * qbConfig - the configuration file structure contains settings such as session duration for the React Kit, keys for interacting with AI features, and so on. * loginData - information about the logged-in user. Let's add the QuickBlox UIKit chat layer - QuickBloxUIKitDesktopLayout - to the markup of the main component of the application. If we do not specify a sessionToken in accountData, it means that the login and session start process occurs within our application. In this case, it is necessary to perform the following fifth step in our instruction. However, if the session start and application login process is already performed in another application, such as on a server, and we already have a ready sessionToken, then we can skip the next step. ### Step 5. Configure QuickBloxUIKitProvider To configure QuickBloxUIKitProvider and use QuickBloxUIKit in your application, follow these steps: Initialize DataContext: 1. To work with QuickBlox, it is necessary to initialize the UIKit react DataContext. It contains important data and settings for using QuickBloxSDK. The DataContext connects various components of the application and provides them access to shared data. ```TypeScript TypeScript theme={null} const qbUIKitContext: QBDataContextType = React.useContext(qbDataContext); const [isUserAuthorized, setUserAuthorized] = React.useState(false); const [isSDKInitialized, setSDKInitialized] = React.useState(false); const prepareSDK = async (): Promise => { // check if we have installed SDK if ((window as any).QB === undefined) { if (QB !== undefined) { (window as any).QB = QB; } else { let QBLib = require('quickblox/quickblox.min'); (window as any).QB = QBLib; } } const APPLICATION_ID = QBConfig.credentials.appId; const AUTH_KEY = QBConfig.credentials.authKey; const AUTH_SECRET = QBConfig.credentials.authSecret; const ACCOUNT_KEY = QBConfig.credentials.accountKey; const CONFIG = QBConfig.appConfig; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY, CONFIG); }; ``` 2. We need to add user authentication, so to do this, we will introduce two states and use the useEffect hook: ```TypeScript TypeScript theme={null} const [authorized, setAuthorized] = React.useState(false); const [initedSDK, setInitedSDK] = React.useState(false); ...... useEffect(() => { if (!isSDKInitialized) { prepareSDK().then(result => { QB.createSession(currentUser, async function (errorCreateSession: any, session: any) { if (errorCreateSession) { console.log('Create User Session has error:', JSON.stringify(errorCreateSession)); } else { const userId: number = session.user_id; const password: string = session.token; const paramsConnect = { userId, password }; QB.chat.connect(paramsConnect, async function (errorConnect: any, resultConnect: any) { if (errorConnect) { console.log('Can not connect to chat server: ', errorConnect); } else { const authData: AuthorizationData = { userId: userId, password: password, userName: currentUser.login, sessionToken: session.token }; await qbUIKitContext.authorize(authData); setSDKInitialized(true); setUserAuthorized(true); } }); } }); }).catch( e => { console.log('init SDK has error: ', e) }); } }, []); ...... ``` After implementing all the steps, your App.tsx file should look like this. You can compare it this your code or copy it instead of. ```TypeScript TypeScript theme={null} import React, { useEffect } from 'react'; // @ts-ignore import * as QB from "quickblox/quickblox"; import { QuickBloxUIKitProvider, qbDataContext, QuickBloxUIKitDesktopLayout, LoginData, AuthorizationData, QBDataContextType, } from 'quickblox-react-ui-kit'; import { QBConfig } from './QBconfig'; import './App.css'; function App() { const currentUser: LoginData = { login: '', password: '', }; const qbUIKitContext: QBDataContextType = React.useContext(qbDataContext); const [isUserAuthorized, setUserAuthorized] = React.useState(false); const [isSDKInitialized, setSDKInitialized] = React.useState(false); const prepareSDK = async (): Promise => { // check if we have installed SDK if ((window as any).QB === undefined) { if (QB !== undefined) { (window as any).QB = QB; } else { let QBLib = require('quickblox/quickblox.min'); (window as any).QB = QBLib; } } const APPLICATION_ID = QBConfig.credentials.appId; const AUTH_KEY = QBConfig.credentials.authKey; const AUTH_SECRET = QBConfig.credentials.authSecret; const ACCOUNT_KEY = QBConfig.credentials.accountKey; const CONFIG = QBConfig.appConfig; QB.init(APPLICATION_ID, AUTH_KEY, AUTH_SECRET, ACCOUNT_KEY, CONFIG); }; useEffect(() => { if (!isSDKInitialized) { prepareSDK().then(result => { QB.createSession(currentUser, async function (errorCreateSession: any, session: any) { if (errorCreateSession) { console.log('Create User Session has error:', JSON.stringify(errorCreateSession)); } else { const userId: number = session.user_id; const password: string = session.token; const paramsConnect = { userId, password }; QB.chat.connect(paramsConnect, async function (errorConnect: any, resultConnect: any) { if (errorConnect) { console.log('Can not connect to chat server: ', errorConnect); } else { const authData: AuthorizationData = { userId: userId, password: password, userName: currentUser.login, sessionToken: session.token }; await qbUIKitContext.authorize(authData); setSDKInitialized(true); setUserAuthorized(true); } }); } }); }).catch( e => { console.log('init SDK has error: ', e) }); } }, []); return (
{ // React states indicating the ability to render UI isSDKInitialized && isUserAuthorized ? :
wait while SDK is initializing...
}
); } export default App; ``` You should run the application using the command: ```Powershell Powershell theme={null} npm start ```