Skip to main content

Third-Party Messenger Developer Guide

User Account Management and Message Routing

When a user sends a message through an External System that reaches Flowable, the following checks are made:

  • does a UserAccount exist for the provided externalUserId and externalSystemId?
  • is the UserAccount active for the provided externalUserId and externalSystemId?
  • is there an (only one) open conversation available for this userAccountId

Only then is the message routed to the right conversation based on the conversation matching this userAccountId.

Therefore, during the onboarding procedure a User Account needs to be created if it doesn't exist.

Create User Account

In case no UserAccount is found for the given externalUserId and externalSystemId then the following method is executed:

InboundMessageAccountService.messageReceivedNoAccount(InboundMessage inboundMessage)

Out of the box Flowable sends an internal system event _flowableEngageMessageReceivedNoAccount that can be listened to and handled with the Event Registry within Cases or Processes. However, this interface can be implemented to your own needs.

In a client onboarding lifecycle case all the required checks can be done to create the required information to handle the message, such as:

  • Checking if there is already a user available for this externalUserId or is the creation of a new user required?
  • Creating the user account for this user with the required information (externalUserId and externalSystem (WhatsApp, WeChat or LINE))
  • Defining the type of conversation which needs to be created for this user (group conversation or private conversation with one user) (including participant selection)
  • Creation of a conversation that is linked to the UserAccount

To create a User Account, first a User Account Definition is required that is referenced by your user account. You can configure your User Account Definition by adding a <filename>.user.account.json file in your classpath under com/flowable/user-accounts/default or com/flowable/user-accounts/custom.

In this file you can configure multiple user accounts in a JSON-based format. An example of a user account could be:

[
{
"key": "default-messenger-user-account",
"name": "Default messenger user account",
"description": "A default user account for external messenger users like ones from WhatsApp, WeChat, LINE, etc",
"initialState": "inactive",
"initialSubState": "onboarding",
"initialType": "messenger"
}
]

This user account example sets the user account type to messenger, the initial status to inactive and the sub state to onboarding.

Now, a User Account can be created by executing following Java code when required:

UserAccount userAccount = userAccountService.createNewUserAccountBuilder()
.name(name)
.userId(userId)
.externalUserId(externalUserId)
.businessAccountId(businessAccountId)
.userAccountDefinitionKey("default-messenger-user-account")
.subType(externalSystem)
.save();

Next, a conversation needs to be created where the above UserAccount is going to be linked with.

Conversation conversation = conversationService.createConversationBuilder()
.ownerId(externalUserParticipant)
.addParticipant(externalUserParticipant)
.addParticipant(participant1)
.addParticipant(participant2)
.name(conversationName)
.type(ConversationTypes.GM)
.start();

Lastly, the Conversation needs to be linked with the UserAccount as following:

conversationService.linkConversationWithUserAccount(conversation.getId(), userAccount.getId());

Please note, that only one UserAccount can be linked with a conversation.

Handle Inactive User Accounts

An inactive user account is determined by there being no link to a userId or by status flag. It is possible to set the status of an UserAccount to INACTIVE.

In this cases the following method can be executed:

InboundMessageAccountService.messageReceivedInactiveAccount(InboundMessage inboundMessage, UserAccount userAccount)

Also here, in the default implementation (LoggingInboundMessageAccountService) this method just gives a warning that the user account is not active and the message is ignored.

Same, as above, this interface can be implemented to your own needs and, for instance, the client onboarding lifecycle case can be searched and notified (for example, by triggering a listener) that a new message has been received from an inactive user account.

Template Messages Configuration

There are two formats for messages that can be sent via the WhatsApp Business API or the WeChat Business Account:

  • Free text & multimedia messages can be sent within a time window (WhatsApp: 24 hours, WeChat: 48 hours) after the external user sends a message to the business. The response messages form the business within this time window are free.
  • Template messages (predefined messages) must be used when the time window (after 24 / 48 hours) has expired.

Sending Template Messages with WhatsApp

It is really important to mention that each template needs to be configured and approved by WhatsApp (see WhatsApp Template Documentation).

Once templates have been configured for the WhatsApp Business Account they can be used within your application.

Flowable supports WhatsApp templates and can be sent by following code:

whatsAppExternalConversationSystemAdapter.createTemplateMessageBuilder()
.externalUserId(externalUserId)
.templateId(whatsAppTemplateCode)
.addTemplateTextParameters(templateParameters)
.language(templateLanguage)
.messageId(externalMessageId)
.send();

The whatsAppExternalConversationSystemAdapter is a bean that can be Autowired in your Spring Boot application.

As sending templates over WhatsApp costs money, the WhatsApp Adapter supports you to deactivate the actual sending of template messages in the WhatsApp Adapter, and only logging them by setting this property:

flowable.adapter.whatsapp.jms.template-message-logging-only=true

This is recommended only for development environments.

Sending Template Messages with WeChat

Templates also need to be pre-configured in your WeChat Business Account and they can also be used within your application.

Flowable supports WeChat templates and can be sent by following code:

WeChatOutboundTemplateMessageDto outboundMessageDto = new WeChatOutboundTemplateMessageDto();
outboundMessageDto.setExternalUserId(externalUserId);
outboundMessageDto.setTemplateId(weChatTemplateCode);
outboundMessageDto.setParameters(templateParameters);
outboundMessageDto.setMessageId(externalMessageId);
jmsMessagingOperations.convertAndSend(weChatProperties.getOutbound(), outboundMessageDto);

The jmsMessagingOperations and the weChatProperties are beans that can be Autowired in your Spring Boot application.

Query Templates and Expose Templates to Flowable Internal Users

Please consider that if you send such templates, they will only be visible in the external account. If you want to make the processed template content visible also to your Flowable conversation you need to:

  • process the template content manually and set it as your message content
  • send a Message of the type TEMPLATE and subtype EXTERNAL_TEMPLATE to the conversation.

For processing the template content it is necessary to deploy the template content as Template Variation within the Flowable Template Engine.

To process the template content now you can use the Template Engine like this:

Map<String, Object> variant = MapUtil.mapOf("templateCode", templateCode);
TemplateProcessingResult processingResult = templateService.processTemplate(templateKey, variant, payload);

The bean templateService can also be Autowired in this case. The template model with the key templateKey requires a variation parameter based on templateCode and multiple variations for the different templateCode for each template message. Furthermore, a payload can be passed to the template engine to process the data in case of parameter-based template messages.

At this point the processed template is available and needs to be sent to the conversation for the internal Flowable Users.

Sending a message of the type TEMPLATE and subtype EXTERNAL_TEMPLATE ensures that this message is only forwarded to the internal users and not to the external user. Thus, the client will only receive one template message from the official external system Business API.

To send such a message this code can be implemented:

Message internalMessage = messageService.createMessageBuilder()
.mainContent(processingResult.getProcessedContent())
.type(DataTypes.TEMPLATE)
.subType(DataSubTypes.EXTERNAL_TEMPLATE)
.send(conversation.getId());

By making this template available in the template engine, these template variations can now be queried, visualised and selected within a Form.

Mapping of External Templates with Flowable Templates

The templateCodes provided by the external system are not very human-readable. Therefore, Flowable enables a name mapping through the application properties between external system templates and internal template variations templateCode.

flowable.external-system.whatsapp.templates.myInternalKey1=myExternalWhatsAppTemplateKey1
flowable.external-system.whatsapp.templates.myInternalKey2=myExternalWhatsAppTemplateKey2
flowable.external-system.whatsapp.templates.myInternalKeyN=myExternalWhatsAppTemplateKeyN
flowable.external-system.wechat.templates.myInternalKey1=myExternalWeChatTemplateKey1
flowable.external-system.wechat.templates.myInternalKey2=myExternalWeChatTemplateKey2
flowable.external-system.wechat.templates.myInternalKeyN=myExternalWeChatTemplateKeyN

The mapping feature comes quite handy when you have several external systems integrated at the same time with the same template content. In the above example the myInternalKey1 is the same key independent of the external system.

Now, with the beans whatsAppProperties or weChatProperties, the external templateCode can be retrieved and used for the external system as described above (weChatTemplateCode, whatsAppTemplateCode).

String weChatTemplateId = weChatProperties.getTemplates().get(templateCode);
String whatsAppTemplateId = whatsAppProperties.getTemplates().get(templateCode);

Handle the Template-Window (24 Hours / 48 Hours)

Flowable provides the possibility to configure timeout handling processes in order to proactively show to the internal user that the free text & multimedia messages time window has expired.

flowable.external-system.whatsapp.timeout.enabled=true
flowable.external-system.whatsapp.timeout.processDefinitionKey=P98_WhatsAppTimeoutProcess
flowable.external-system.whatsapp.timeout.rescheduleDuration=PT24H
flowable.external-system.wechat.timeout.enabled=true
flowable.external-system.wechat.timeout.processDefinitionKey=P99_WeChatTimeoutProcess
flowable.external-system.wechat.timeout.rescheduleDuration=PT48H

The timeout handling can be enabled for each external system separately by the enabled property. This starts a process instance of the defined processDefinitionKey (if it was not yet started) to handle the timeout.

Within the process it is necessary to model an intermediate timer event that waits for the timeout to expire. The process can then proactively handle the timeout (for example, by notifying the user that the timeout for the external user has expired). Usually, sticky action messages are instantiated to the conversation.

Every time a new message from the external user reaches Flowable, either:

  • a new process instance is created when a process instance is not yet running for this User Account.
  • the timer of the currently running process instance is rescheduled by the duration configured by the rescheduleDuration property.

The rescheduleDuration needs to be a textual representation of a Duration as described here.

Activation of Message Types

As soon as a message reaches Flowable, the platform first checks if the message type is marked as supported. Support for different message types can be activated and deactivated by setting the following properties to true and false.

In all cases where the message type is not enabled, Flowable returns a message to the external user that this message cannot be delivered.

WhatsApp Message Type Configuration

flowable.external-system.whatsapp.enabled-message-types.text=true
flowable.external-system.whatsapp.enabled-message-types.image=true
flowable.external-system.whatsapp.enabled-message-types.video=true
flowable.external-system.whatsapp.enabled-message-types.document=true
flowable.external-system.whatsapp.enabled-message-types.audio=true
flowable.external-system.whatsapp.enabled-message-types.voice=true

Handling of WhatsApp Content

Prerequisite for handling WhatsApp content is the related WhatsApp Adapter Configuration.

When sending content (such as image, video, document, audio and voice messages) the WhatsApp Business API will not send the plain content to the WhatsApp Adapter, but only an ID of the content that needs to be retrieved from the WhatsApp Business API (see WhatsApp Webhook Documentation).

Now, the WhatsApp Adapter notifies Flowable that such a message arrived. Flowable will check if it’s a supported message type and if it is, then Flowable will need to request the content data from the WhatsApp Adapter. The WhatsApp Adapter retrieves the content from the WhatsApp Client and returns it to Flowable.

WhatsApp On Premise

Whatsapp Adapter Outbound Flow

When receiving content the WhatsApp Adapter is notified from the WhatsApp Business API and sends a JMS message to Flowable. Flowable will then retrieve the content item through the /whatsapp-api/content endpoint of the Whatsapp Adapter.

Whatsapp Adapter Inbound Flow

WhatsApp Cloud

In cloud mode, the Flowable WhatsApp Adapter communicates directly to the WhatsApp Servers. Whatsapp Adapter Outbound Flow

When receiving content the WhatsApp Adapter is notified from the WhatsApp Business API and sends a JMS message to Flowable. Flowable will then retrieve the content item through the /whatsapp-api/content endpoint of the Whatsapp Adapter.

Whatsapp Adapter Inbound Flow

WeChat Message Types Configuration

flowable.external-system.wechat.enabled-message-types.text=true
flowable.external-system.wechat.enabled-message-types.image=false
flowable.external-system.wechat.enabled-message-types.video=false
flowable.external-system.wechat.enabled-message-types.document=false
flowable.external-system.wechat.enabled-message-types.audio=false
flowable.external-system.wechat.enabled-message-types.voice=false

Currently, only text and image messages are supported for the WeChat Adapter.

LINE Message Type Configuration

flowable.external-system.line.enabled-message-types.text=true
flowable.external-system.line.enabled-message-types.image=true
flowable.external-system.line.enabled-message-types.video=true
flowable.external-system.line.enabled-message-types.audio=true
flowable.external-system.line.enabled-message-types.voice=true

Handling of LINE Content

Prerequisite for handling LINE content is the related LINE Adapter Configuration.

When sending content (such as image, video, document, audio and voice messages) the LINE API will not send the plain content to the LINE Adapter, but only an ID of the content that needs to be retrieved from the LINE API (see LINE Webhook Documentation).

Now, the LINE Adapter notifies Flowable that such a message arrived. Flowable will check if it’s a supported message type and if it is, then Flowable will need to request the content data from the LINE Adapter. The LINE Adapter retrieves the content from the LINE Rest API and returns it to Flowable.

When receiving content the LINE Adapter is notified from the LINE API and sends a JMS message to Flowable. Flowable will then retrieve the content item through the /line-api/content endpoint of the LINE Adapter.

The flow for receiving content from LINE to Flowable is similar as the flow when receiving content from WhatsApp to Flowable.

Handling of Flowable to LINE Content

The way content is send to LINE differs to the way it is done with WhatsApp. The way LINE works is by sending the URLs of the location of the content and then the LINE mobile application directly accesses the content through those URLs. This means that the request for retrieving the content is done from the user devices. Therefore, we created a mechanism that will generate a special token that allows accessing the specific content through a publicly accessible API for a limited period. There is a hook point (LineOutboundMediaContentProvider) which allows implementors to provide a different way of making the content publicly accessible. Due to this you have to explicitly enable the outbound content sending from Flowable to LINE.