Status

NOT STARTED

Owner

Gavin Zhang (Unlicensed) Andre Laksmana (Unlicensed)

Contributors

User a4ed6 User bf1a8 ru.wang (Unlicensed)

Due date

Tickets

Epic: SAF-1404 - BO: Genesys integration Backlog

  1. SAF-1264 - chat in app support Selected for Development

On this page

Terms

Name

Description

Customer

The Safibank mobile app user.

Agent

The backoffice agent.

Conversation

Here refers to the chat between a customer and a backoffice agent.

ACD

Automatic communication distribution is a system that intelligently routes interactions based on agent availability, caller input, agent skill levels, volume of interactions, time of day, agent groups, trunk line, costs, priority, or other variables.

Queue

An ACD queue is a workgroup queue that is set up to deliver ACD calls. ACD calls are routed to the appropriate Workgroup based on caller input. All members of that Workgroup (call agents) are expected to have a core set of skills required to handle any call on that queue.

Inbound message

A message sent to Genesys.

Outbound message

A message sent by Genesys.

Message receipt

Acknowledge a message is received. A message receipt is also an inbound or outbound message, used to tell the message producer the current status of the message.

Mongodb Realm

Realm is an embedded, object-oriented database that lets you build real-time, always-on applications.

Background

We don’t want customer’s app to interact with Genesys cloud directly. We also want to make sure the conversations between customer mobile app and Genesys cloud are monitored for security purpose.

Architecture overview

The only way to chime in on a conversation between mobile app and Genesys cloud, is using Open Messaging.

Open messaging is a lightweight integration that facilitates messaging with third-party systems and external messaging services. With open messaging you can leverage Genesys Cloud's asynchronous ACD capabilities to route inbound open messages to the right queues and agents.

How Backoffice genesys-gateway interacts with Genesys Cloud

Here the Middleware stands for Backoffice’s genesys-gateway.

How a mobile app interacts with Genesys-gateway

Simply put:

  1. Backoffice (more precisely, genesys-gateway) will provide a webhook to receive outbound messages from Genesys Cloud.

  2. Backoffice will use Genesys SDK to push an inbound message to Genesys Cloud.

  3. Mobile apps will not communicate with Backoffice directly, but instead there will be Realm as middleman.

Implementation

(tick) Prerequisites

The first step in setting up open messaging is to create an integration. There are two ways to create an integration:

  1. Create from Genesys Cloud Admin UI.

  2. Send a request to POST /api/v2/conversations/messaging/integrations/open.

Brave:

{
  "name": "Open Messaging Integration for Brave environment",                                  // required
  "outboundNotificationWebhookUrl": "https://api.smallog.tech/genesys-gateway/webhook/conversations/outbound/messages",  // required, webhook URL
  // required, Genesys uses this token to compute a signature for a request and then include the signature in X-Hub-Signature-256 header in the request to webhook.
  "outboundNotificationWebhookSignatureSecretToken": "OUTBOUND_NOTIFICATION_WEBHOOK_SIGNATURE_SECRET_TOKEN",
  "webhookHeaders": {                            // optional
    "YOUR-HEADER-KEY-1": "YOUR-HEADER-VALUE-1",
    "YOUR-HEADER-KEY-2": "YOUR-HEADER-VALUE-2",
    ...
  }
}

Tangled:

{
  "name": "Open Messaging Integration for tangled environment",                                  // required
  "outboundNotificationWebhookUrl": "https://api.sfdvwork.xyz/genesys-gateway/webhook/conversations/outbound/messages",  // required, webhook URL
  "outboundNotificationWebhookSignatureSecretToken": "OUTBOUND_NOTIFICATION_WEBHOOK_SIGNATURE_SECRET_TOKEN", // required, see description above.
  "webhookHeaders": {                            // optional
    "YOUR-HEADER-KEY-1": "YOUR-HEADER-VALUE-1",
    "YOUR-HEADER-KEY-2": "YOUR-HEADER-VALUE-2",
    ...
  }
}

The token is used to encrypt the request to webhook. Genesys cloud uses this token to generate a signature for the request, and puts both token and signature in http request’s headers. In webhook, we’ll verify and ensure the request is not tampered.

To add Realm SDK in the application (https://www.mongodb.com/docs/realm/sdk/kotlin/quick-start/#quick-start---kotlin-sdk ), we have to:

  1. add plugin id("io.realm.kotlin") version "1.6.1" in gradle

  2. add io.realm.kotlin:library-sync and org.jetbrains.kotlinx:kotlinx-coroutines-core as dependencies

(tick) Generate OAuth token

In order to call Genesys Cloud APIs, we must generate a token for the calls.

Genesys provides SDK so we can use directly. See ph.safibank.genesysgateway.client.GenesysClient in genesys-gateway.

(blue star) Process inbound messages

Since mobile app will send (or exactly speaking, sync between realms) the chat messages to Realm, for Backoffice we need to consume the messages from Realm then forward to Genesys Cloud using Genesys RESTful APIs.

1. Consume messages from Realm

To receive any updates on chat messages in Realm, refer to https://www.mongodb.com/docs/realm/sdk/kotlin/realm-database/react-to-changes/#register-a-realmobject-change-listener .

2. Convert a Realm message to Genesys inbound message

3. Send the inbound message to Genesys Cloud

We can use either of the two ways to push an inbound message to Genesys Cloud:

  1. POST /api/v2/conversations/messages/inbound/open or

  2. ConversationsApi.postConversationsMessagesInboundOpen (Recommended)

  1. If this is the first message of a conversation, which means the customer starts a new conversation, we need to store the customerId-conversationId combination (and timestamp) in database.

  2. There should be a composite index on ‘customerId’ and timestamp columns.

  3. Nice-to-have: An Ably message should be able to indicate whether it’s the first message of a conversation or not. CC Andre Laksmana (Unlicensed)

More sample inbound messages can be found here.

{
  "id": "<Open Message ID>",
  "channel": {
    "platform": "Open",
    "type": "Private",
    "messageId": "<External Service Message ID>",  // should be able to use a random UUID
    "to": {
      "id": "<Integration ID>"
    },
    "from": {
      "nickname": "Messaging User",
      "id": "messaging-user@externalservice.com",  // this should be the customer id
      "idType": "email",
      "image": "https://externalservice.com/profiles/messaging-user.png",
      "firstName": "Messaging",
      "lastName": "User"
    },
    "time": "2021-01-21T16:47:07.592Z"
  },
  "type": "Text",
  "text": "Inbound message",
  "direction": "Inbound"
}

Process outbound messages

1. Webhook

SAF-1901 - Create webhook to receive Genesys outbound messages Ready for Testing

To receive any conversation (outbound) messages from Genesys Cloud, we need to provide a RESTful API in genesys-gateway as the webhook.

POST /genesys/webhook/conversations/outbound/messages

The sample message payloads can be found here.

{
  "id": "<Messaging Platform Message ID>",
  "channel": {
    "platform": "Open",
    "type": "Private",
    "to": {
      "id": "messaging-user@externalservice.com",  // the customer id
      "idType": "email"
    },
    "from": {
      "id": "<Integration ID>"
    },
    "time": "2021-01-21T16:47:07.592Z"
  },
  "type": "Text",
  "text": "Outbound message text!",
  "direction": "Outbound"
}

2. Validate an outbound message

To validate if a message is really from Genesys Cloud, we need to get the signature from the X-Hub-Signature-256 request header, then compare with the SHA value of message:

Validate an outbound message.

Javascript code sample:

const crypto = require('crypto');

// integration - the integration object
// normalizedMessage - the NormalizedMessage payload
// request - webhook request object

const normalizedMessage = request.data;
const signature = request.headers['X-Hub-Signature-256'];
const secretToken = integration.outboundNotificationWebhookSignatureSecretToken;
const messageHash = crypto.createHmac('sha256', secretToken)
    .update(JSON.stringify(normalizedMessage))
    .digest('base64');

if (`sha256=${messageHash}` !== signature) {
    throw new Error("Webhook Validation Failed! Throw this away.");
} else {
    processMessages(normalizedMessage);
}

3. Construct a Realm message and sync with customer’s mobile

To be determined for the message format.

To sync any updates with mobile apps, refer to https://www.mongodb.com/docs/realm/sdk/kotlin/realm-database/create/create-a-new-object/#create-a-new-object---kotlin-sdk or https://www.mongodb.com/docs/realm/sdk/kotlin/realm-database/update/upsert-an-object/#upsert-an-object---kotlin-sdk .

Acknowledge a message

Refer to Message receipts for more details. A message receipt is also an inbound or outbound message.

When an outbound message is received by the customer’s mobile app, we will push an inbound message receipt to Genesys Cloud to notify that the message is delivered successfully.

When an inbound message is accepted by Genesys Cloud, we will get a message receipt from webhook, saying the inbound message is delivered.

(question) Conversation history API)

Can mobile app retrieve from Realm ?

Since we have stored customerId-conversationId combinations in database, and there is an index on ‘customerId' column, it should be very fast to query the conversationIds for a given customer.

1. Query the conversations for a given customer and time period

GET /genesys/{customerId}/conversations?from=2023-01-20&to=2023-01-25 // suppose time is in +8 timezone

We get the conversation ids by querying database.

2. Construct the conversation history

Now we are able to get the conversations one by one using:

GET /api/v2/conversations/messages/{conversationId}, its response contains all of the messages of a conversation.

To be determined for the response. Andre Laksmana (Unlicensed)

If there are a lot of conversation ids, it would be very slow to load all of the conversations for these ids.

Issue of onboarding conversations

A customer who is onboarding, may want to chat with a Backoffice agent to get help because s/he meets with problems during onboarding. After onboarded, s/he also wants to be able to see his/her previous conversations.

In order to achieve this, the customer’s mobile app must tell Backoffice the formal customer id after onboarded.

The process would be like this:

  1. A customer is onboarding but meets with problems.

  2. The customer chats with a Backoffice agent.

    1. The customer’s mobile app creates a temporary customer id since at this moment the customer is not a regular customer and doesn’t have a formal customer id.

    2. Backoffice (genesys-gateway) queries Genesys Cloud to find the conversation using the message id, and store the customerId-conversationId combination into database.

  3. After the customer completes the onboarding, the customer’s mobile app send the temporary customer id and new formal customer id to Backoffice(either by Ably message or RESTful API).

  4. Backoffice (genesys-gateway) updates the customerId-conversationId combination in database.

Message formats

Type

From

To

Format

Inbound

Mobile app

Ably

Andre Laksmana (Unlicensed)

Inbound

genesys-gateway

Genesys

Outbound

Genesys

genesys-gateway

Outbound

genesys-gateway

Ably

Outbound

Ably

Mobile app

Andre Laksmana (Unlicensed)

Resources

  1. Open Messaging - Genesys Cloud Developer Center

  2. https://help.mypurecloud.com/articles/about-messaging/

  3. https://zh-cn-help.mypurecloud.com/articles/about-messaging/

  4. https://www.mongodb.com/docs/realm/

Attachments: