Home
/
Desk
/
iOS

Key functions

This page explains the key functions of Sendbird Desk consisting of how to create, handle, and close a ticket from your client app.


Message types

There are two types of messages in Desk. User messages are messages sent from an agent or a customer while Admin messages are messages sent from the Desk server without a specific sender.

Among User messages, the following three subtypes of messages that have additional implementation processes are grouped into Rich messages: URL preview, confirmation request for ticket closing, and feedback request messages. Their custom message type is specified as SENDBIRD_DESK_RICH_MESSAGE and the type of the message is determined in the message.data as below.

Rich messages

Message typemessage.customTypemessage.data

URL previews

SENDBIRD_DESK_RIGH_MESSAGE

SENDBIRD_DESK_URL_PREVIEW

Confirmation request for ticket closing

SENDBIRD_DESK_RICH_MESSAGE

SENDBIRD_DESK_INQUIRE_TICKET_CLOSURE

Feedback request

SENDBIRD_DESK_RICH_MESSAGE

SENDBIRD_DESK_CUSTOMER_SATISFACTION

Admin messages are classified into Notification messages and System messages. System messages have SENDBIRD_DESK_ADMIN_MESSAGE_CUSTOM_TYPE as their custom message type that makes it to be distinguished from notification messages. They also have subtypes specified in the message.data property.


Create a ticket

Implement the SBDSKTicket.createTicket() method to create a new ticket either before or after the customer’s initial message. Once a ticket is successfully created in the Desk server, you can access the ticket and its channel in the ticket.channel through the callback from the server. Until a customer sends the first message, agents can’t see the ticket on the dashboard. When a conversation starts, the ticket is assigned to an available agent by the Desk server while messages are sent and received through the Chat SDK.

Light Color Skin
Copy
SBDSKTicket.createTicket(withTitle:TICKET_TITLE, userName: USER_NAME) { (ticket, error) in
    guard error == nil else {
        // Handle error.
    }

    // The ticket is created. Agents and customers can chat with each other by sending a message through the ticket.channel.sendUserMessage() or sendFileMessage().
}

You can use the following parameters when creating a ticket.

List of arguments

ArgumentTypeDescription

TICKET_TITLE

string

Specifies the title of the ticket.

USER_NAME

string

Specifies the name of the user who submits or receives the ticket.

GROUP_KEY

string

Specifies the identifier of a specific team.

customFields

[String: String]

Specifies additional information of the ticket that consists of key-value custom items. Only custom fields already registered in Settings > Ticket fields on your dashboard can be used as a key.

PRIORITY

string

Specifies the priority value of the ticket. Higher values stand for higher priority. Valid values are LOW, MEDIUM, HIGH and URGENT.

RELATED_CHANNEL_URLS

array

Specifies group channels in Sendbird Chat platform that are related to this ticket and consists of channel URLs and channel names. Up to three related channels can be added.

Light Color Skin
Copy
var customFields = [String: String]()
customFields["product"] = "desk"
customFields["line"] = "14"
customFields["select"] = "option2"

SBDSKTicket.createTicket(withTitle: TICKET_TITLE, userName: USER_NAME, groupKey: "cs-team-1", customFields: customFields, priority: PRIORITY, relatedChannels: RELATED_CHANNEL_URLS) { (ticket, error) in
    guard error == nil else {
        // Handle error.
    }

    // The ticket is created with parameters.
}

Add custom information to a ticket

Use the ticket.setCustomFields() method to add additional information about a ticket.

Light Color Skin
Copy
var customFields = [String: String]()
customFields["product"] = "Desk"
customFields["line"] = "\(30)"

ticket.setCustomFields(customFields) { (error) in
    guard error == nil else {
        // Handle error.
    }

    // Custom fields for the ticket are set.
    // Some fields can be ignored if their keys aren't registered in the dashboard.
}

Note: Only custom fields registered in Settings > Ticket fields of your dashboard can be used as a key.


Add custom information to a customer

Use the setCustomerCustomFields() method of the SBDSKMain to make your customers add additional information about themselves.

Note: Only custom fields registered in Settings > Customer fields of your dashboard can be used as a key.

Light Color Skin
Copy
var customFields = [String: String]()
customFields["gender"] = "Female"
customFields["age"] = "\(30)"

SBDSKMain.setCustomerCustomFields(customFields) { (error) in
    guard error == nil else {
        // Handle error.
    }
}

// Custom fields for the customer are set.
// Some fields can be ignored if their keys aren't registered in the dashboard.

Related channels indicate group channels in Sendbird Chat platform that are related to a ticket. When creating a ticket, pass one or more URLs (channel_url) of the related group channel as an argument to the relatedChannels parameter in the SBDSKTicket.createTicket() method. To update the related channels, use the ticket.setRelatedChannels() instead. The ticket.relatedChannels property in the callback indicates the group channel object of related channels and it contains channel names and their URLs.

Light Color Skin
Copy
ticket.setRelatedChannels(RELATED_CHANNEL_URLS) { (error) in
    guard error == nil else {
        // Handle error.
    }

    // The ticket.relatedChannels property has been updated.
}

Note: Up to three related channels can be added per ticket.


Add URL previews

With URL previews, your application users can meet their expectations of what they’re going to get before they open the link during the conversations.

To use URL previews, every text message should be checked if it includes any URLs. Second, parse the HTML source of the URL to look for OpenGraph meta tags. Then, set the extracted OG meta data as a JSON object and stringify the object to pass it as an argument to a data parameter in the SBDGroupChannel.update(userMessage, messageText, data, customType, completionHandler) method. The updated message with URL preview is delivered to the client app through the channel(_ sender: SBDBaseChannel, didUpdate message:SBDBaseMessage) delegate method of the SBDChannelDelegate.

Light Color Skin
Copy
ticket.channel?.sendUserMessage(TEXT, completionHandler: { (userMessage, error) in
    guard let userMessage = userMessage, error == nil else {
        // Handle error.
    }

    let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
    let match = detector?.firstMatch(in: userMessage.message, options: [], range: NSMakeRange(0, userMessage.message.count))
    guard let url = match?.url else {   // No matching URL
        return
    }

    URLSession().dataTask(with: url) { (data, response, error) in
        guard error == nil, let data = data else {
            // Handle error.
        }

        guard let httpResponse = response as? HTTPURLResponse,
        let contentType = httpResponse.allHeaderFields["Content-Type"] as? String,
            contentType.contains("text/html") else { return }
        let htmlBody = String(data: data, encoding: .utf8)

        // Extract ogTitle, ogImage, ogUrl, ogSiteName, ogDescription from htmlBody.
        // Refer to Open Graph Protocol(https://ogp.me/), and other open source implementations of OG tag parsing for further details.

        let urlPreview = ["type": "SENDBIRD_DESK_URL_PREVIEW",
            "body": [
                "title": ogTitle,
                "image": ogImage,
                "url": ogUrl,
                "site_name": ogSiteName,
                "description": ogDescription
            ]
        ] as [String: Any]
        // Stringified JSON object let jsonData = try? JSONSerialization.data(withJSONObject: urlPreview, options: [])

        ticket.channel?.update(userMessage, messageText: userMessage.message, data: jsonData?.base64EncodedString(), customType: "SENDBIRD_DESK_RICH_MESSAGE", completionHandler: { (userMessage, error) in
            guard error == nil else {
                // Handle error.
            }
            ...

            // Pass data to SBDGroupChannel.update(userMessage, messageText, data, customType, completionHandler) method.
        })
    }
})

There are various methods to extract OG meta data from the html body. You can also refer to our GitHub repository for the method we're using.

In the SBDChannelDelegate's channel(_ sender: SBDBaseChannel, didUpdate message: SBDBaseMessage) delegate method, you can find the data for URL preview in the UserMessage.data property as below.

Light Color Skin
Copy
{
    "type": "SENDBIRD_DESK_URL_PREVIEW",
    "body": {
        "url": "https://sendbird.com/",
        "site_name": "Sendbird",
        "title": "A Complete Chat Platform, Messaging and Chat SDK and API",
        "description": "Sendbird's chat, voice and video APIs and SDKs connect users through immersive, modern communication solutions...",
        "image": "https://6cro14eml0v2yuvyx3v5j11j-wpengine.netdna-ssl.com/wp-content/uploads/sendbird_thumbnail.png"
    }
}

Receive system messages

Admin messages are customizable messages that are sent by the system, and there are two types of admin messages. Notifications are messages that are sent and displayed to both customers and agents, such as welcome messages or delay messages. System messages are messages sent and displayed to agents in the Ticket details view when a ticket has some changes, such as changes in ticket status and assignee.

Note: You can customize notifications in Settings > Triggers, and system messages in Settings > System messages on your dashboard.

When the client app receives the message through the channel(_ sender:SBDBaseChannel, didReceive message: SBDBaseMessage) method of the SBDChannelDelegate, system messages are distinguished from notification messages by the value of the message.custom_type, and their subtype is specified in the message.data as below.

Light Color Skin
Copy
{
    "message_id" : 40620745,
    "type": "ADMM",
    "custom_type": "SENDBIRD_DESK_ADMIN_MESSAGE_CUSTOM_TYPE",
    "data": "{\"type\": \"SYSTEM_MESSAGE_TICKET_ASSIGNED_BY_SYSTEM\", \"ticket\": <Ticket Object>}",
    ...
    "message": "The ticket is automatically assigned to Cindy.",
    ...
}

Note: The transfer appears only when the data property has SYSTEM_MESSAGE_TICKET_TRANSFERRED_BY_AGENT.

System messages are intended to be displayed for agents only. Refer to the following sample code to avoid displaying them to your customers.

Light Color Skin
Copy
public static func isVisible(message: SBDBaseMessage) -> Bool {
    if let message = message as? SBDAdminMessage, let data = Data(base64Encoded: message.data), data.isEmpty == false {
        let isSystemMessage = (message.customType == "ADMIN_MESSAGE_CUSTOM_TYPE")

        let dataObject = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
        let type = dataObject?["type"] as? String
        return !isSystemMessage &&
            type != EVENT_TYPE_ASSIGN &&
            type != EVENT_TYPE_TRANSFER &&
            type != EVENT_TYPE_CLOSE
    }

    return true
}

Request confirmation to close a ticket

While admins have permission to directly close a ticket, agents can either close a ticket as admins do or ask customers whether to close a ticket, depending on the agent permission setting. The confirmation request message can have three types of state as below.

Confirmation states

StateDescription

WAITING

Set when an agent sends a confirmation request message.

CONFIRMED

Set when a customer agrees to close the ticket. (true)

DECLINED

Set when a customer declines to close the ticket. (false)

When a customer replies to the message, the response true (agree) or false (decline) is sent to the Desk server as CONFIRMED or DECLINED by calling the SBDSKTicket.confirmEndOfChat() method.

Light Color Skin
Copy
SBDSKTicket.confirmEndOfChat(with: USER_MESSAGE, confirm: true|false) { (ticket, error) in
    guard error == nil else {
        // Handle error.
    }

    // You can update the UI of the message. For example, you can hide YES and NO buttons.
}

Sendbird server notifies the customer’s client app of updates through the channel(_ sender: SBDBaseChannel, didUpdate message: SBDBaseMessage) method of SBDChannelDelegate.

Light Color Skin
Copy
func channel(_ sender: SBDBaseChannel, didUpdate message: SBDBaseMessage) {
    SBDSKTicket.getByChannelUrl(sender.channelUrl) { (ticket, error) in
        guard error == nil else {
            // Handle error.
        }

        if let data = Data(base64Encoded: message.data), data.isEmpty == false {
            let dataObject = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
            let type = dataObject?["type"] as? String

            let isClosureInquired = (type == "SENDBIRD_DESK_INQUIRE_TICKET_CLOSURE")
            if isClosureInquired {
                let closureInquiry = dataObject?["body"] as? [String: Any]
                let state = closureInquiry?["state"] as? String

                switch state {
                    case "CONFIRMED":
                    // Implement your code for the UI when the customer confirms to close the ticket.
                    case "DECLINED":
                    // Implement your code for the UI when the customer declines to close the ticket.
                    case "WAITING":
                    // Implement your code for the UI when there is no response from the customer.
                    default: break
                }
            }
        }
    }
}

Note: You can find the stringified JSON object of the following in the message.data property within the channel(_ sender: SBDBaseChannel, didUpdate message: SBDBaseMessage) delegate method of the SBDChannelDelegate.

Light Color Skin
Copy
{
    "type": "SENDBIRD_DESK_INQUIRE_TICKET_CLOSURE",
    "body": {
        "state": "CONFIRMED"
    }
}

Request customer feedback

You can send a message to customers right after closing a ticket to ask whether they are satisfied with the support provided through the ticket. When the Customer satisfaction rating feature is turned on in your dashboard, customers get a message asking to give a score and leave a comment as feedback. The message can have two states as below.

Request states

StateDescription

WAITING

Set when an agent sends a customer feedback request message.

CONFIRMED

Set when a customer sends a response.

When a customer replies to the message, the score and comment are sent to the Desk server by calling the ticket.submitFeedback() method. Then, the state of the confirmation request message is changed to CONFIRMED.

Light Color Skin
Copy
ticket.submitFeedback(with: USER_MESSAGE, score: SCORE, comment: COMMENT) { (ticket, error) in
    guard error == nil else {
        // Handle error.
    }
    ...
}

Sendbird server notifies the customer’s client app of updates through the channel(_ sender: SBDBaseChannel, didUpdate message: SBDBaseMessage) delegate method of the SBDChannelDelegate.

Light Color Skin
Copy
func channel(_ sender: SBDBaseChannel, didUpdate message: SBDBaseMessage) {
    SBDSKTicket.getByChannelUrl(sender.channelUrl) { (ticket, error) in
        guard error == nil else {
            // Handle error.
        }

        if let data = Data(base64Encoded: message.data), data.isEmpty == false {
            let dataObject = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
            let type = dataObject?["type"] as? String

            let isFeedbackMessage = (type == "SENDBIRD_DESK_CUSTOMER_SATISFACTION")
            if isFeedbackMessage {
                let closureInquiry = dataObject?["body"] as? [String: Any]
                let state = closureInquiry?["state"] as? String

                switch state {
                    case "CONFIRMED":
                    // Implement your code for the UI when there is a response from the customer.
                    case "WAITING":
                    // Implement your code for the UI when there is no response from the customer.
                    default: break
                }
            }
        }
    }
}

Note: You can find the stringified JSON object of the following in the message.data property within the channel(_ sender: SBDBaseChannel, didUpdate message: SBDBaseMessage) delegate method of SBDChannelDelegate.

Light Color Skin
Copy
{
    "type": "SENDBIRD_DESK_CUSTOMER_SATISFACTION",
    "body": {
        "state": "CONFIRMED",
        "customerSatisfactionScore": 3,                             // Score range: 1 to 5
        "customerSatisfactionComment": "It was really helpful :)."  // Comment is optional.
    }
}

Reopen a closed ticket

A closed ticket can be reopened by using the ticket.reopen() method.

Light Color Skin
Copy
ticket.reopen { (ticket, error) in
    guard error == nil else {
        // Handle error.
    }

    ...
}

Retrieve a list of tickets

You can retrieve a list of the current customer’s open and closed tickets by using the SBDSKTicket.getOpenedList() and SBDSKTicket.getClosedList().

Note: Only 10 tickets can be retrieved per request by message creation time in descending order.

getOpenedList()
getClosedList()
Light Color Skin
Copy
SBDSKTicket.getOpenedList(withOffset: OFFSET) { (tickets, hasNext, error) in
    guard error == nil else {
        // Handle error.
    }

    // offset += tickets.size(); for the next tickets.
    // Implement your code to display the ticket list.
}
Light Color Skin
Copy
SBDSKTicket.getClosedList(withOffset: OFFSET) { (tickets, hasNext, error) in
    guard error == nil else {
        // Handle error.
    }

    // offset += tickets.size(); for the next tickets.
    // Implement your code to display the ticket list.
}

For tickets set with custom fields, you can add a filter to the getOpenList() and getClosedList() to sort tickets by keys and values of custom fields.

Light Color Skin
Copy
let customFieldFilter = ["subject": "doggy_doggy"]

SBDSKTicket.getOpenedList(withOffset: OFFSET, customFieldFilter: CUSTOM_FIELD_FILTER) { (tickets, hasNext, error) in
    guard error == nil else {
        // Handle error.
    }

    // offset += tickets.length; for the next tickets.
    // Implement your code to display the ticket list.
}

Retrieve a ticket

You can retrieve a specific ticket with its channel URL.

Light Color Skin
Copy
SBDSKTicket.getByChannelUrl(channel.channelUrl) { (ticket, error) in
    guard error == nil else {
        // Handle error.
    }
    ...
}

Display open ticket count

You can display the number of open tickets on your client app by using the SBDSKTicket.getOpenCount().

Light Color Skin
Copy
SBDSKTicket.getOpenCount { (count, error) in
    guard error == nil else {
        // Handle error.
    }

    // Implement your code with the value of the "count" parameter.
}

Close a ticket

Use the SBDSKTicket.close() to allow customers to directly close a ticket on their client app so that agents can quickly switch to other customer inquiries without delay or a customer’s confirmation.

Light Color Skin
Copy
ticket.close(withComment: closeComment) { (ticket, error) in
    guard error == nil else {
        // Handle error.
    }

    // Implement your code to close a ticket.
}

Note: Only active and idle tickets can be closed by customers.