Exchange messages

Kommo is well-known as a messenger-based CRM, so we decided to have a system specified to provide an integrating chat service with Kommo called Online chat. Online chat handles sending and receiving messages using our Chat API.

Contents

  1. Exchanging messages business logic
  2. Highload queues
  3. Payload (sending, receiving)(text, media)
  4. Example on sending message in PHP
  5. Importing messages

Sending and receiving messages

Let’s begin with a client sending a message to a Kommo user via the messenger application. A webhook might be triggered carrying the message details or any other way. We will not discuss the process from the messenger side, as everyone uses their own solution, but we will explain the messaging procedure from our side.

Online chat uses queues to manage messages; we advise using them in your integration to achieve better performance. Here is a top view of the systems integration.

If you have implemented queues in your integration, which considered best practice, you should place the message in the queue, and you can send an acknowledgment of the message reception.

The integration starts processing the message by sending a request to our Chat API sending messages method. The request should determine the chat from the messenger service side. If there is no chat between the client and the user or the existing one is closed, you should create a new one.

The request must define only the message’s sender as the contact who sent the message when it comes from the client to Kommo.

The request should carry the body of the message as it was received according to its type. If it contains links, they should be processed and sent. The message attachments, if provided, should be extracted, checked, saved, and shipped.

Upon the successful response, the message will be added to Kommo in the client chat upon the successful response. If anything goes wrong, a response with the error will be returned and should be processed.

To sum it up, your job is to understand the response you got from the messenger service and create a request to send it to the Online chat.

To discuss the other direction for messages, i.e., messages from the user to the client, you should know that Kommo sends a webhook to the registered URL stated in the chat channel specifications. The integration should parse the webhook to get the message information. The information contains the chat identifier from the messenger service side. A new chat should be created if there is no chat between the client and the user or the existing one is closed. It also contains the sender, and receiver information, message body, and type. It’s expected to add a request to send a message to the corresponding queue and then form the request to send the message in the messenger service way.

Highload queues

Your integration should process high loads and work with many messages and leads; for this, we recommend using queues. The queuing system is a principle, not a specific technology, and queues ensure scalability, increasing the application speed.

The queue server keeps a list of tasks the main application sends. A task is just information about what needs to be done. An external solution to implement a queuing system is optional. Simplicity and the availability of ready-made solutions will make it faster.

Each time a message comes from a client, a task is created to implement it in a handler and submitted to the receiver queue. An instance of the corresponding worker grabs a task from the queue and executes the logic associated with it. The same architecture should be used for sending messages with the sender queue.

Payload

To create the message payload, you should define its type first, then fill in the expected fields. Supported types are text, contact, file, video, picture, voice, audio, sticker, and location.

When sending messages from the client to the Kommo user, you should specify both the sender and receiver objects when creating the message payload for messages sent from the Kommo user to the client. However, indicating only the sender’s information when sending a message from the client to the user is enough.

After creating the payload, it is necessary to calculate the MD5 hash and indicate it in the header in lowercase.

You should sign the message to ensure its delivery. The signature is formed from the method name, the headers, and the URL.

The MD5 and the X-Signature should be sent as the request headers, in addition to the date and time when the request was generated and the “application/json” as the request data type.

Finally, you can decide whether or not there will be a notification about the message.

Text

The payload consists of the fields specified in the sending/receiving messages method. Please indicate the message type as text and fill it in the text field, which is also a part of other message types.

And the JSON for the received message is

{
   "event_type": "new_message",
   "payload": {
       "timestamp": 1676301850,
       "msec_timestamp": 1676301850924,
       "msgid": "8e3e7640-49af-4448-a2c6-000000000001",
       "conversation_id": "con-8e3e7640-49af-4448-a2c6-000000000001",
       "sender": {
           "id": "con-1376265f-86df-4c49-a0c3-000000000001",
           "profile": {
               "phone": "+18305803077"
           },
           "name": "Adam"
       },
       "message": {
           "type": "text",
           "text": "Hello!"
       },
       "silent": false
   }
}

The JSON for the sent message

{
   "event_type": "new_message",
   "payload": {
       "timestamp": 1680016921,
       "msec_timestamp": 1680016921666,
       "msgid": "8e3e7640-49af-4448-a2cc-000000000002",
       "conversation_id": "con-8e3e7640-49af-4448-a2cc-000000000001",
       "sender": {
           "id": "con-1376265f-86df-4c49-a0cc-000000000003",
           "ref_id": "b0bc49f0-ec21-4463-965f-1fe1d4cd5b89",
           "name": "Lama Saqqour"
       },
       "receiver": {
           "id": "con-1376265f-86df-4c49-a0cc-000000000001",
           "profile": {
               "phone": "+18305803077"
           },
           "name": "Adam"
       },
       "message": {
           "type": "text",
           "text": "Hello Adam!"
       },
       "silent": true
   }
}

Media

Kommo online chat supports sending rich messages of various types in addition to text messages to make the chat more practical, such as contact, file, video, picture, voice, audio, sticker, and location. To send a file, a picture, a voice, an audio, or a sticker, you should send its URL to a third party, and you can send its name and size. If you send a contact, you should send its name and phone; if you send a location, you should send longitude and latitude. More about media in rich messages.

{
   "event_type": "new_message",
   "payload": {
       "timestamp": 1679657944,
       "msec_timestamp": 1679657944383,
       "msgid": "8e3e7640-49af-4448-a2cc-000000000009",
       "conversation_id": "con-8e3e7640-49af-4448-a2cc-000000000001",
       "sender": {
           "id": "con-1376265f-86df-4c49-a0cc-000000000001",
           "profile": {
               "phone": "+18305803077"
           },
           "name": "Adam"
       },
       "message": {
           "type": "file",
           "text": "Please find attached the offer file",
           "media": "https://www.w3.org/dummy.pdf",
           "file_name": "offer"
       },
       "silent": false
   }
}

Example on sending message in PHP

class SendMessageToAmoJoUseCase{
    private AmoJoConversationRepository $amoJoConversationRepository;
    private AmoJoClient $amoJoClient;
    private SourcesRepository $sourcesRepository;

    public function handle(SendMessageToAmoJoTask $task): void
    {
        $amoJoConversation = $this->amoJoConversationRepository
            ->getByMsgrClientIdAndKommoAccountId(
                $task->getMsgrClientId(),
                $widgetSettings->getKommoAccountId()
            );

        if ($amoJoConversation === null) {
            $amoJoConversation = $this->amoJoConversationRepository->create();

            $clientHash = sha1($task->getMsgrClientId());
            $amoJoConversation->setConversationId($clientHash);
            $amoJoResponse = $this->amoJoClient->createConversation(
                $clientHash,
                $clientHash,
                $widgetSettings->getAmoJoScopeId()
            );

            if (!isset($amoJoResponse['id'])) {
                throw BusinessLogicException::create('Create conversation error');
            }            $amoJoConversation->setAmoJoConversationId($amoJoResponse['id']);
           $amoJoConversation->setAmoJoClientId($amoJoResponse['user']['id']);
        }

        if ($sourceId !== null) {
            $source = $this->sourcesRepository
                ->findOneByIdAndAccountId(
                    $sourceId,
                    $widgetSettings->getAmoCrmAccountId()
                );
        }

        $sourceExternalId = null;
        if ($source !== null) {
            $sourceExternalId = $source->getExternalSourceId();
        }
        $amoJoConversation->save();

        $result = $this->amoJoClient->sendMessage(
            $task->getBody(),
            $amoJoConversation->getConversationId(),
            $widgetSettings->getAmoJoScopeId(),
            $amoJoConversation->getName(),
            $amoJoConversation->getAvatarUrl(),
            '',
            $sourceExternalId
        );

        $task->setSuccess($result);
    }
} 

Importing messages

To answer clients faster, managers sometimes use the chat service application to send messages and don’t wait to open their Kommo account. You can import a whole chat or some messages to the account, and our API adds each message with its actual time.

You can also allow users to import messages sent previously by both the client and the user using the same method when sending messages. All imported chats/leads fall into the incoming leads stage.

You can perform the importing process in two steps: checking the contacts’ existence, creating social profiles, updating the existing ones, importing the chats and leads, and linking them to the corresponding contact.

We recommend performing the import without notifications to managers or creating an incoming lead for all messages except the last one (the most recent). We also advise you to activate the duplicate control function provided by Kommo to avoid duplicating the leads. To do this, pass the body parameter payload[silent] a true value for all messages except for the last one that passes the payload[silent] a false value and create a notification about the successful completion of the import. This way, an incoming message will be created for the last message, and only one notification will come. Thus not creating unnecessary disturbance for the user.

You should also discuss the date from which you want to grant the user the ability to import chats and leads, the message types you like to import, and the limitations on the media files.

When importing messages from the integration bot, hooks are not sent.

class ImportMessageUseCase
{
private SendMessageToAmoJoTask $task;
  public function handle($data): void
  {
    	$message = $data['payload'];
    	$isLast = $message['is_last'] ?? false;
    	$silent = !$isLast;
    	$message['is_silent'] = $silent;
    	$task['message'] = $message;
    	$this->SendMessageToAmoJoUseCase->handle($task);
  }
} 

And the payload for such messages, except the last one, will be like this

{
   "event_type": "new_message",
   "payload": {
       "timestamp": 1670490330,
       "msec_timestamp": 1670490330161,
       "msgid": "f1065e3b-0ec6-427b-b97c-883329acbe3f",
       "conversation_id": "62ef74a4-80c5-403d-93d9-bada6302810d",
       "sender": {
           "id": "b0bc49f0-ec21-4463-965f-1fe1d4cd5a90",
           "name": "Lama",
           "ref_id": "76fc2bea-902f-425c-9a3d-dcdac4766090"
       },
       "receiver": {
           "id": "b0bc49f0-ec21-4463-965f-1fe1d4cd5a89",
           "avatar": "https://example.com/users/avatar.png",
           "name": "Adam",
           "profile": {
               "phone": "+18305803077",
               "email": "example.client@example.com"
           },
           "profile_link": "https://example.com/profile/example.client"
       },
       "message": {
           "type": "text",
           "text": "Hello from Kommo! It is Thursday already!"
       },
       "silent": true
   }
}

After managing chats, it’s time to benefit from the chats in CRM.