В данном разделе мы рассмотрим основной функционал любой интеграции в API Чатов, а также разберем остальной функционал,
который интеграция может реализовать по необходимости.
Мы разберем в этой статье как создать самую простую, но рабочую интеграцию с API Чатов, а также функции,
которые могут быть полезны для больших, тиражируемых решений.
Любая интеграция, для корректной работы с API чатов, должна реализовать следующий функционал:
Следующие функции будут уже расширением базового функционала интеграции:
В данной статье будут представлены примеры кода на PHP.
Все примеры можно скачать по ссылке.
Для уменьшения дублирования кода представим,
что у нас уже есть файл со вспомогательными методами helpers.php:
<?php
/**
* Расчитываем хэш тела запроса
* @param string $body Тело запроса в строковом представлении (json)
*
* @return string
*/
function createBodyChecksum(string $body): string
{
return md5($body);
}
/**
* Расчитываем подпись запроса
*
* @param string $secret Секретный ключ вашего канала
* @param string $checkSum Рассчитанный хэш тела запроса
* @param string $apiMethod Адрес вызываемого метода API
* @param string $httpMethod HTTP метод запроса
* @param string $contentType Передаваемый тип данных
*
* @return string
*/
function createSignature(
string $secret,
string $checkSum,
string $apiMethod,
string $httpMethod = 'POST',
string $contentType = 'application/json'
): string {
$str = implode("\n", [
strtoupper($httpMethod),
$checkSum,
$contentType,
date(DateTimeInterface::RFC2822),
$apiMethod,
]);
return hash_hmac('sha1', $str, $secret);
}
/**
* Подготавливаем заголовки для запроса
*
* @param string $checkSum Рассчитанный хэш тела запроса
* @param string $signature Рассчитанная подпись запроса
* @param string $contentType Передаваемый тип данных
*
* @return array
*/
function prepareHeaderForCurl(
string $checkSum,
string $signature,
string $contentType = 'application/json'
): array {
$headers = [
'Date' => date(DateTimeInterface::RFC2822),
'Content-Type' => $contentType,
'Content-MD5' => strtolower($checkSum),
'X-Signature' => strtolower($signature),
'User-Agent' => 'amoCRM-Chats-Doc-Example/1.0'
];
foreach ($headers as $name => $value) {
$curlHeaders[] = $name . ": " . $value;
}
return $curlHeaders;
}
/**
* Выполняем запрос к API Чатов
*
* @param string $apiMethod Запрашиваемый метод API
* @param string $requestBody Тело запроса
* @param array $requestHeaders Заголовки запроса
* @param string $httpMethod HTTP метод запроса
*/
function execCurl(
string $apiMethod,
string $requestBody,
array $requestHeaders,
string $httpMethod = 'POST'
): void {
$curl = curl_init();
$curlOptions = [
CURLOPT_URL => 'https://amojo.amocrm.ru' . $apiMethod,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $httpMethod,
CURLOPT_HTTPHEADER => $requestHeaders,
];
if (!empty($requestBody)) {
$curlOptions[CURLOPT_POSTFIELDS] = $requestBody;
}
curl_setopt_array($curl, $curlOptions);
$response = curl_exec($curl);
$error = curl_error($curl);
$info = curl_getinfo($curl);
curl_close($curl);
if ($error) {
echo "cURL Error #:" . $error;
} else {
echo "Status: " . $info['http_code'] . PHP_EOL;
echo $response . PHP_EOL;
}
}
После создания канала, необходимо выполнить подключение канала к аккаунту.
Для этого необходимо воспользоваться методом подключения канала.
Важно отметить, если ваш канал еще не является публичным, то возможность подключения ограниченна перечисленными при регистрации аккаунтами.
После подключения канала к аккаунту, вы получите scope_id, который необходимо использовать для дальнейших запросов.
Рассмотрим пример кода:
<?php
include __DIR__ . '/helpers.php';
// Секретный ключ канала
$channelSecret = 'f2d7f8704eff95087ed45b23ba99c0b5aac8278e';
// ID канала в сервисе чатов, который подключаем к каналу
$channelId = '344a5002-f8ca-454d-af3d-396180102ac7';
// Тело запроса
$requestBody = [
'account_id' => '52e591f7-c98f-4255-8495-827210138c81',
'title' => 'ChatsIntegration',
'hook_api_version' => 'v2',
];
$jsonBody = json_encode($requestBody);
$checkSum = createBodyChecksum($jsonBody);
$apiMethod = sprintf('/v2/origin/custom/%s/connect', $channelId);
// Составим подпись
$signature = createSignature(
$channelSecret,
$checkSum,
$apiMethod
);
// Подготовим заголовки
$curlHeaders = prepareHeaderForCurl($checkSum, $signature);
echo 'POST ' . $apiMethod . PHP_EOL;
foreach ($curlHeaders as $header) {
echo $header . PHP_EOL;
}
echo PHP_EOL . $jsonBody . PHP_EOL . PHP_EOL;
// Выполним запрос
execCurl($apiMethod, $jsonBody, $curlHeaders);
POST https://amojo.amocrm.ru/v2/origin/custom/344a5002-f8ca-454d-af3d-396180102ac7/connect
Date: Wed, 15 Dec 2021 23:12:55 +0000
Content-Type: application/json
Content-MD5: 2fbcb3c652b61e24e004daddfc73d0ce
X-Signature: 81211856ce0c5095f3e1a90c0a38dc9d736cfd55
User-Agent: amoCRM-Chats-Doc-Example/1.0
{
"account_id": "52e591f7-c98f-4255-8495-827210138c81",
"title": "ChatsIntegration",
"hook_api_version": "v2"
}
{
"account_id": "52e591f7-c98f-4255-8495-827210138c81",
"scope_id": "344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81",
"title": "ChatsIntegration",
"hook_api_version": "v2"
}
После подключения канала к аккаунту, вы получите возможность пользоваться методом API.
Один из первых методов, с которым вы столкнетесь – отправка сообщения из чата в amoCRM.
Для отправки необходимо воспользоваться методом добавления сообщений.
Данный метод позволяет как добавлять входящие сообщение, так и исходящие, в данном разделе рассмотрим кейс входящего сообщения из стороннего чата в amoCRM.
Также стоит отметить, что API чатов не принимает несколько сообщений с одинаковым payload[msgid].
А также при передаче payload[conversation_id], которого ранее не было в системе – будет создан новый чат.
Рассмотрим пример кода:
<?php
include __DIR__ . '/helpers.php';
// Секретный ключ канала
$channelSecret = 'f2d7f8704eff95087ed45b23ba99c0b5aac8278e';
// Scope ID, который был получен при подключении канала в аккаунт
$scopeId = '344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81';
// Тело запроса, отправим сообщение в amoCRM, которое написал нам клиент
$requestBody = [
'event_type' => 'new_message',
'payload' => [
'timestamp' => time(),
'msec_timestamp' => round(microtime(true) * 1000),
'msgid' => 'my_int-5f2836a8ca481',
'conversation_id' => 'my_int-d5a421f7f218',
'sender' => [
'id' => 'my_int-1376265f-86df-4c49-a0c3-a4816df41af8',
'avatar' => 'https://images.pexels.com/photos/10050979/pexels-photo-10050979.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
'profile' => [
'phone' => '+79151112233',
'email' => 'example.client@example.com',
],
'profile_link' => 'https://example.com/profile/example.client',
'name' => 'Вася клиент',
],
'message' => [
'type' => 'text',
'text' => 'Сообщение от клиента',
],
'silent' => false,
],
];
$jsonBody = json_encode($requestBody);
$checkSum = createBodyChecksum($jsonBody);
$apiMethod = sprintf('/v2/origin/custom/%s', $scopeId);
// Составим подпись
$signature = createSignature(
$channelSecret,
$checkSum,
$apiMethod
);
// Подготовим заголовки
$curlHeaders = prepareHeaderForCurl($checkSum, $signature);
echo 'POST ' . $apiMethod . PHP_EOL;
foreach ($curlHeaders as $header) {
echo $header . PHP_EOL;
}
echo PHP_EOL . $jsonBody . PHP_EOL . PHP_EOL;
// Выполним запрос
execCurl($apiMethod, $jsonBody, $curlHeaders);
POST https://amojo.amocrm.ru/v2/origin/custom/344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81
Date: Thu, 16 Dec 2021 13:15:29 +0000
Content-Type: application/json
Content-MD5: 223ef85def871bc7cb58aa6f02f0a26e
X-Signature: 29b49e9eaf03f13a66ecaff315f2855b31b25eee
User-Agent: amoCRM-Chats-Doc-Example/1.0
Разберем некоторые из параметров запроса, которые могут вызывать вопросы:
Для передачи сообщений с медиа вложениями, необходимо в message[type] указать необходимый тип и передать структуру в соответствии со спецификацией метода.
{
"event_type": "new_message",
"payload": {
"timestamp": 1639660529,
"msec_timestamp": 1639660529379,
"msgid": "my_int-5f2836a8ca481",
"conversation_id": "my_int-d5a421f7f218",
"sender": {
"id": "my_int-1376265f-86df-4c49-a0c3-a4816df41af8",
"avatar": "https://images.pexels.com/photos/10050979/pexels-photo-10050979.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500",
"profile": {
"phone": "+79151112233",
"email": "example.client@example.com"
},
"profile_link": "https://example.com/profile/example.client",
"name": "Вася клиент"
},
"message": {
"type": "text",
"text": "Сообщение от клиента"
},
"silent": false
}
}
В ответе вы получите ID сообщения на стороне amoCRM (new_message[msgid]).
{
"new_message": {
"msgid": "8e6afe4e-a08a-4801-b6cb-37963c1a6f3c",
"ref_id": "my_int-5f2836a8ca481"
}
}
Когда пользователь отправляет сообщение из amoCRM в ваш канал, мы отправляем на указанный адрес webhook.
После получения вебхука, вам необходимо обработать его и отправить сообщение в мессенджер.
Если вы получили сообщение, которое интегрированный мессенджер не может принять, вам необходимо обновить статус сообщения на ошибочный.
Также мы настойчиво рекомендуем обрабатывать сообщения и отправлять в мессенджер вне контекста запроса.
То есть, при получении вебхука, вы проверяете подпись запроса, а дальше, например, ставите задачу на обработку в очередь и отдаете ответ на вебхук.
Дальше, уже в асинхронном режиме, вы обрабатываете сообщение, производите вашу бизнес-логику и отправляете сообщение.
В данный момент мы ждем ответа на хук не больше 5 секунд. Для того чтобы мы посчитали хук успешно отправленным,
ваша интеграция должна ответить на запрос с http-кодом 200.
API Чатов имеет механизмы приоритизации, если интеграция начинает долго отвечать на хуки или не отвечать вовсе,
мы можем вынести отдельно от основного потока сообщений в медленный поток, в котором хуки могут приходить с задержкой.
Приоритет рассчитывается динамически и зависят от разных факторов, интеграция может перейти как в более быструю очередь, так и в более медленную.
Описание формата Webhook’ов от API Чатов (Webhook Reference)
Рассмотрим пример кода, который обрабатывает структуру полученного хука:
<?php
include __DIR__ . '/helpers.php';
// Определим методы, которые выполняют бизнес-логику
function saveTextMessage(
string $messageReceiver,
string $chatId,
string $text
) {
// Сохраняем текстовое сообщение
}
function savePictureMessage(
string $messageReceiver,
string $chatId,
array $file,
string $messageText
) {
// Сохраняем изображение
}
function saveFileMessage(
string $messageReceiver,
string $chatId,
array $file,
string $messageText
) {
// Сохраняем файл
}
function downloadFile(string $url): array
{
// сохраняем файл по ссылке на диск, возвращаем информацию о файле
return [];
}
function setErrorDeliveryStatus($messageId) {
//Обновляем статус доставки сообщения с информацией, что оно не обработано
}
// Секретный ключ канала
$channelSecret = 'f2d7f8704eff95087ed45b23ba99c0b5aac8278e';
if ($_SERVER ['REQUEST_METHOD'] !== 'POST') {
throw new RuntimeException('Unsupported request');
}
//Полученное тело запроса
$str = file_get_contents('php://input');
$body = stream_get_contents(STDIN);
if (empty($body)) {
throw new RuntimeException('Empty body');
}
$signature = hash_hmac('sha1', $str, $channelSecret);
// Проверяем подпись
if (!isset($_SERVER['HTTP_X_SIGNATURE']) || $signature !== $_SERVER['HTTP_X_SIGNATURE']) {
echo 'Invalid hook';
die;
}
$hookBody = json_decode($body, true);
if (!$hookBody) {
throw new RuntimeException('Unsupported body');
}
// ID аккаунта в API чатов
$accountId = $hookBody['account_id'];
// ID сообщения в API чатов
$messageId = $hookBody['message']['message']['id'];
// Тип сообщения
$messageType = $hookBody['message']['message']['type'];
// Тип сообщения
$messageText = $hookBody['message']['message']['text'];
// ID чата, в которое было отправлено сообщение на стороне интеграции
$messageChatId = $hookBody['message']['conversation']['client_id'];
// ID чата, в которое было отправлено сообщение на стороне amoCRM
$messageAmoCrmChatId = $hookBody['message']['conversation']['id'];
// Ссылка на прикрепленный к сообщению файл
$fileLink = $hookBody['message']['message']['media'] ?? null;
// ID получателя на стороне интеграции
$receiverId = $hookBody['message']['receiver']['id'];
switch ($hookBody['message']['message']['type']) {
case 'text':
saveTextMessage(
$receiverId,
$messageChatId,
$messageText
);
break;
case 'picture':
$downloadedFile = downloadFile($fileLink);
savePictureMessage(
$receiverId,
$messageChatId,
$downloadedFile,
$messageText
);
break;
case 'file':
$downloadedFile = downloadFile($fileLink);
saveFileMessage(
$receiverId,
$messageChatId,
$downloadedFile,
$messageText
);
break;
default:
setErrorDeliveryStatus($messageId);
throw new RuntimeException('Unsupported message type');
break;
}
{
"account_id": "52e591f7-c98f-4255-8495-827210138c81",
"time": 1639572261,
"message": {
"receiver": {
"id": "2ed64e26-70a1-4857-8382-bb066a076219",
"phone": "79161234567",
"email": "example.client@example.com",
"client_id":"my_int-1376265f-86df-4c49-a0c3-a4816df41af8"
},
"sender": {
"id": "76fc2bea-902f-425c-9a3d-dcdac4766090"
},
"conversation": {
"id": "8e4d4baa-9e6c-4a88-838a-5f62be227bdc",
"client_id":"my_int-d5a421f7f218"
},
"source":{
"external_id":"78001234567"
},
"timestamp": 1639572260,
"msec_timestamp": 1639572260980,
"message": {
"id": "0371a0ff-b78a-4c7b-8538-a7d547e10692",
"type": "picture",
"text": "Текст сообщения Сделка #15926745",
"markup": {
"mode": "inline",
"buttons": [
[
{
"text":"Принять заказ"
},
{
"text":"Отменить заказ"
}
]
]
},
"tag": "",
"media": "https://amojo.amocrm.ru/attachments/image.jpg",
"thumbnail": "https://amojo.amocrm.ru/attachments/image_320x200.jpg",
"file_name": "",
"file_size": 0,
"template": {
"id": 7103,
"content": "Текст сообщения {{lead.name}}",
"params": [
{
"key": "{{lead.id}}",
"value": "15926745"
}
]
}
}
}
}
После получения сообщения, интеграция передает его в мессенеджер.
Статус сообщения может измениться: сообщение может быть не доставлено или может стать прочитанным.
Через API чатов можно обновить статус сообщения, а также передать информацию об ошибке.
Для этого необходимо воспользоваться методом обновления статуса сообщения.
Рассмотрим пример кода, в котором мы обновим сообщение 079e44fb-fc22-476b-9e8a-421b688ec53b и укажем статус "Не доставлено":
<?php
include __DIR__ . '/helpers.php';
// Секретный ключ канала
$channelSecret = 'f2d7f8704eff95087ed45b23ba99c0b5aac8278e';
// Scope ID, который был получен при подключении канала в аккаунт
$scopeId = '344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81';
// ID сообщения, которое было получено в хуке
$messageId = '079e44fb-fc22-476b-9e8a-421b688ec53b';
// Тело запроса, обновим сообщение с информацией об ошибке
$requestBody = [
'msgid' => '079e44fb-fc22-476b-9e8a-421b688ec53b',
'delivery_status' => -1,
'error_code' => 905,
'error' => 'Error text'
];
$jsonBody = json_encode($requestBody);
$checkSum = createBodyChecksum($jsonBody);
$apiMethod = sprintf('/v2/origin/custom/%s/%s/delivery_status', $scopeId, $messageId);
// Составим подпись
$signature = createSignature(
$channelSecret,
$checkSum,
$apiMethod
);
// Подготовим заголовки
$curlHeaders = prepareHeaderForCurl($checkSum, $signature);
echo 'POST ' . $apiMethod . PHP_EOL;
foreach ($curlHeaders as $header) {
echo $header . PHP_EOL;
}
echo PHP_EOL . $jsonBody . PHP_EOL . PHP_EOL;
// Выполним запрос
execCurl($apiMethod, $jsonBody, $curlHeaders);
POST https://amojo.amocrm.ru/v2/origin/custom/344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81/079e44fb-fc22-476b-9e8a-421b688ec53b/delivery_status
Date: Thu, 16 Dec 2021 12:58:40 +0000
Content-Type: application/json
Content-MD5: a0bbbac729c6341f0a521e2db7a8a236
X-Signature: ca51f810785031385a801f41103244713c1a2352
User-Agent: amoCRM-Chats-Doc-Example/1.0
{
"msgid": "079e44fb-fc22-476b-9e8a-421b688ec53b",
"delivery_status": 2,
"error_code": 905,
"error": "Error text"
}
Метод не возвращает ответа
В случае отключения интеграции, необходимо выполнить отключение канала от аккаунта.
Для этого необходимо воспользоваться методом отключение канала.
Рассмотрим пример кода:
<?php
include __DIR__ . '/helpers.php';
// Секретный ключ канала
$channelSecret = 'f2d7f8704eff95087ed45b23ba99c0b5aac8278e';
// ID канала в сервисе чатов, который подключаем к каналу
$channelId = '344a5002-f8ca-454d-af3d-396180102ac7';
// Тело запроса
$requestBody = [
'account_id' => '52e591f7-c98f-4255-8495-827210138c81',
];
$jsonBody = json_encode($requestBody);
$checkSum = createBodyChecksum($jsonBody);
$apiMethod = sprintf('/v2/origin/custom/%s/disconnect', $channelId);
// Составим подпись
$signature = createSignature(
$channelSecret,
$checkSum,
$apiMethod,
'DELETE'
);
// Подготовим заголовки
$curlHeaders = prepareHeaderForCurl($checkSum, $signature);
echo 'DELETE ' . $apiMethod . PHP_EOL;
foreach ($curlHeaders as $header) {
echo $header . PHP_EOL;
}
echo PHP_EOL . $jsonBody . PHP_EOL . PHP_EOL;
// Выполним запрос
execCurl($apiMethod, $jsonBody, $curlHeaders, 'DELETE');
DELETE https://amojo.amocrm.ru/v2/origin/custom/344a5002-f8ca-454d-af3d-396180102ac7/disconnect
Date: Wed, 15 Dec 2021 23:59:28 +0000
Content-Type: application/json
Content-MD5: 271fa2e4fb0ff84a3ef9689027bd1f38
X-Signature: e1678e3b6aa674b5127f4feb448e6200ce0bfc72
User-Agent: amoCRM-Chats-Doc-Example/1.0
{
"account_id": "52e591f7-c98f-4255-8495-827210138c81"
}
Метод не возвращает тело ответа
Вы можете предоставить другим пользователям amoCRM возможность подключить ваш канал к своему аккаунту.
Для этого необходимо разработать виджет, с помощью которого пользователь сможет связать свой аккаунт с вашим каналом.
Подробнее о разработке виджета вы можете прочитать в документации по виджетам.
В manifest.json, в объект locations вам требуется добавить параметр “lead_sources”.
После этого ваш виджет отобразится в разделе подключения источников.
Для отображения в модальном окне подключения интеграций с WhatsApp, необходимо также добавить в объект locations параметр “whatsapp_modal”.
После открытия окна подключения источников, пользователь открывает виджет и устанавливает его.
Затем производит настройку, например вводит логин и пароль,
JS часть виджета отправляет на ваш сервер связку логин/пароль и ID аккаунта.
ID аккаунта может быть получен виджетом через следующий JS скрипт: APP.constant("account").amojo_id
Ваш сервер проверяет учетные данные и вызывает запрос на подключение нового аккаунта к каналу чатов.
После установки виджета в Digital Pipeline будет создан источник.
Так как виджет может быть установлен и из маркетплейса, в такое случае мы создадим источник автоматически.
Если в интеграции устновлена галка "Множественные источники", источник по-умолчанию создан не будет.
Если интеграция указывает эту галку, мы считаем, что интеграция сама управляет своими источниками.
По-умолчанию, мы считаем, что канал представляет собой подключение одного типа чата (WhatsApp, Viber, Facebook, Онлайн-чат на cайт и другие)
и позволяет возможность передавать сообщения между amoCRM и конечной чат-платформой через интеграцию.
Так как бизнес имеет необходимость разделять общение со своими клиентами по нескольким направлениям
(это может быть несколько телефонных номеров WhatsApp для разных отделов или разные страницы лендингов с онлайн-чатами),
то и канал общения тоже требуется разделять. Для разделения в рамках одного типа чатов используются источники.
Таким образом с помощью источника можно определить, что клиент обратился к бизнесу с определенного лендинга или написал сообщение на телефонный номер конкретного отдела компании.
И в дальнейшем уже выстроить работу с каждым источником индивидуально: распределение по воронкам и работа ботов в зависимости от источника.
В большинстве случаев, интеграция предоставляет связь между amoCRM и одной конкретной чат-платформой.
Таким образом интеграции необходим всего лишь один канал для обеспечения передачи сообщений между аккаунтом amoCRM и чат-платформой.
Если источник требуется только 1, то amoCRM создает источник при установке интеграции.
Если же интеграции требуется несколько источников, это следует учесть при разработке интеграции.
Для реализации поддержки множественных источников интеграции необходимо создать источники через API.
При создании источника, в external_id необходимо указать идентификатор источника на стороне интеграции.
Заданный external_id вы сможете передавать при создании чата или добавлении сообщений, чтобы они были явно связанны с конкретным источником.
Также external_id будет приходить в хуках сообщений, которые были написаны в конкретный канал.
Для того чтобы интеграция поддерживала множественные источники, необходимо, чтобы у интеграции был загружен архив, а в locations в manifest.json присутствовало значение "lead_sources".
Если интеграции не требуется автоматическое создание источника, то в настройках интеграции необходимо установить гавлку "Множественные источники".
Источник "по-умолчанию" позволяет интеграции указать amoCRM, как работать с чатами, у которых не указан источник или передан незарегистрированный источник.
Т.е. он позволяет массово разметить уже существующие чаты при внедрении интеграцией множественных источников.
Источников "по-умолчанию" (с полем default=true
) может быть не более одного на аккаунт для одной интеграции.
Если интеграция не поддерживает работу с множественными источниками, то у нее будет всего 1 источник, который создает amoCRM при добавлении интеграции в качестве источника в Digital Pipeline или установке интеграции.
У таких источников в поле external_id
прописан код виджета. Интеграция может его удалить или назначить другой источник "по-молчанию" через API.
В случае, когда интеграция поддерживает работу с множественными источниками, то amoCRM не будет создавать источник "по-умолчанию" самостоятельно при добавлении интеграции администратором в качестве источника.
И все необходимые источники, интеграция создает уже самостоятельно.
Для этого надо указать соответствующий флаг в настройках интеграции перед передачей на модерацию виджета интеграции.
Что бы сменить источник по-умолчанию, необходимо через API проставить поле default=true
у источника. У предыдущего источника (если он был) поле default будет выставлено в default=false
.
Так как источник "по-умолчанию" не обязателен, то при его удалении ни один другой источник не станет "по-умолчанию" автоматически. Интеграция должна указать новый источник по-умолчанию самостоятельно.
Важный момент
Источник по-умолчанию не привязывается к чатам, если его явно не передавали с сообщениями.
К примеру:
Есть сделка с контактом и чатом, чат не привязан к источнику.
И существуют 2 источника:
Мы назначаем "Отдел технической поддержки" источником по-умолчанию.
Теперь при отправке пользователем сообщения в чат интеграции придет источник "Отдел технической поддержки"
Если назначить источником по-умолчанию "Отдел продаж", то в последующих отправленных из amoCRM сообщениях, external_id в хуке будет равен "Отдел продаж"
Такое поведение будет сохраняться до момента передачи интеграцией источника вместе с сообщением.
Как только интеграция сообщит, что чат относится к конкретному источнику логика источника по-умолчанию перестанет действовать для чата.
Вернуть размеченный чат обратно к логике по-умолчанию уже нельзя.
Данные предостережения стоит рассматривать только в случае, если за интеграцией закреплено более 1 канала и интеграция использует возможность "Написать первым"
Каждая интеграция может управлять только своими источниками, и среди источников интеграции может быть только один источник "по-умолчанию".
Данный момент накладывает дополнительные ограничения на интеграцию при управлении источниками:
В данной ситуации, одним из решений может быть предоставление выбора источника "по-умолчанию" пользователю в настройках интеграции.
Что бы пользователь понимал, что для чатов без явного указания источника будет использоваться конкретный канал и выбранный источник.
Если интеграция не создавала источников самостоятельно,
то при создании неразобранной сделки из чата, в сделке будет указан созданный нами (при подключении интеграции) источник с названием интеграции.
Если интеграция передает в сообщениях несуществующий или удаленный источник, тогда мы проверим, есть ли источник по-умолчанию (свойство источника default=true
).
Если источника по-умолчанию нет – тогда в сделках будет указан источник с названием интеграции (как при создании неразобранного через API неразобранного).
Интеграция может указать в источнике воронку, где будет создано неразобранное при первом входящем сообщении.
Для этого необходимо чтобы внешний идентификатор источника source[external_id]
,
передаваемый в сообщениях
(или при создании чата) совпадал с external_id существующего источника.
В случае когда пользователь начинает переписку с клиентом первым и при этом пользователь выбрал созданный интеграцией источник, то выбранный источник передается интеграции в хуке.
Таким образом интеграция может понять в рамках какого источника была начата переписка, и, к примеру, для WhatsApp интеграций,
отправить клиенту сообщения с конкретного подключенного номера телефона, если администратор подключил несколько номеров.
Источник по-умолчанию может быть использован интеграцией при переходе на множественные источники, особенно если
интеграция поддерживает функционал "Написать первым".
К примеру, имеем исходное состояние:
Есть аккаунт с подключенной интеграцией с чатами, и эта интеграция поддерживает только 1 источник.
На данный момент нам не важно как была установлена интеграция: через Digital Pipeline или маркетплейс.
Интеграция начинает внедрение поддержки множественных источников, этот процесс логически разобьем на этапы:
1 этап
Интеграция умеет работать с API источниками (но не отправляет и не принимает источник в сообщениях amoJo).
На данном этапе интеграция добавляет источник по-умолчанию в аккаунт.
Данный источник по-умолчанию соответствует источнику, который использовался, когда не было поддержки нескольких источников.
Теперь в хуках о сообщениях будет приходить external_id
этого источника для всех чатов, которые явно не закреплены за конкретным источником.
2 этап
Интеграция уже умеет работать с источниками и при отправке сообщений от клиента (при создании чата) указывает external_id.
Все чаты с новыми сообщениями становятся размеченными по переданному в сообщении источнику.
Так же интеграция теперь обрабатывает источник и учитывает его при отправке сообщения от менеджера, в том числе при начале чата с клиентом (функционал "Написать первым").
3 этап
Интеграция позволяет администратору аккаунта добавить (через интеграцию) второй и последующие источники.
Вся переписка по чатам закрепляется за каким-то явно указанным интеграцией источником, все неразмеченные чаты все еще числятся за источником по-умолчанию.
После установки интеграции и получения Access Token, интеграция сможет создавать необходимые источники через API.
Рассмотрим цепочку вызовов:
Представим, что мы разрабатываем интеграцию с WhatsApp с поддержкой нескольких номеров.
Для начала, создадим пару источников для отдела Продаж (+79039876543) и Технической поддержки (+79001234567).
Для идентификации источников возьмем номера телефонов которые, поддерживает наша интеграция: 79039876543 и 79001234567 соответственно.
Телефон технической поддержки будем считать, что пользователь выбрал как источник по умолчанию.
Также добавим номер отдела продаж – как возможный номер для кнопки на сайт.
Для этого отправим запрос на POST /api/v4/sources
POST /api/v4/sources HTTP/1.1
Host: https://example.amocrm.ru
Content-Type: application/json
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbG....
[
{
"name": "Support",
"external_id": "79001234567",
"services": [],
"default": true
},
{
"name": "Sales",
"external_id": "79039876543",
"services": [
{
"type": "whatsapp",
"pages": [
{
"id": "79039876543",
"name": "whatsapp",
"link": "+79039876543"
}
]
}
],
"default": false
}
]
{
"_total_items": 2,
"_embedded": {
"sources": [
{
"id": 3108069,
"name": "Support",
"pipeline_id": 20453,
"external_id": "79001234567",
"services": [],
"default": true,
"request_id": "0",
"_links": {
"self": {
"href": "https://example.amocrm.ru/api/v4/sources/3108069"
}
}
},
{
"id": 3108070,
"name": "Sales",
"pipeline_id": 20453,
"external_id": "79039876543",
"services": [
{
"type": "whatsapp",
"pages": [
{
"id": "79039876543",
"name": "whatsapp",
"link": "+79039876543"
}
]
}
],
"default": false,
"request_id": "1",
"_links": {
"self": {
"href": "https://example.amocrm.ru/api/v4/sources/3108070"
}
}
}
]
}
}
Интеграции необходимо хранить связь external_id и например, номера телефона, на своей стороне.
Теперь мы можем отправлять сообщение в API Чатов с указанием источника, и неразобранное будет создано в воронке, в которой подключен переданный источник.
В данном случае клиент отправил сообщение на номер Отдела продаж и интеграция указала external_id
источника в поле source[external_id]
при отправке сообщения.
POST https://amojo.amocrm.ru/v2/origin/custom/344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81
Date: Fri, 17 Dec 2021 10:59:28 +0000
Content-Type: application/json
Content-MD5: 353178c993f09a8ec5f3eab4093b753e
X-Signature: e895539b52051e1a8d3f89d454c7ad7406705234
User-Agent: amoCRM-Chats-Doc-Example/1.0
{
"event_type": "new_message",
"payload": {
"timestamp": 1639738768,
"msec_timestamp": 1639738768457,
"msgid": "my_int-5f2836a8ca481",
"conversation_id": "my_int-d5a421f7f218",
"sender": {
"id": "my_int-1376265f-86df-4c49-a0c3-a4816df41af8",
"avatar": "https://images.pexels.com/photos/10050979/pexels-photo-10050979.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500",
"profile": {
"phone": "+79151112233",
"email": "example.client@example.com"
},
"profile_link": "https://example.com/profile/example.client",
"name": "Вася клиент"
},
"message": {
"type": "text",
"text": "Сообщение от клиента"
},
"source": {
"external_id": "79039876543"
},
"silent": false
}
}
{
"new_message": {
"msgid": "7779c89e-bb5b-4da0-b802-5de2ab7d4114",
"ref_id": "my_int-5f2836a8ca481"
}
}
После сообщения менеджера клиенту интеграция получит хук, также с указанием источника в поле source[external_id]
По источнику, интеграция должна найти нужный ей мессенджер на своей стороне и отправить ответ клиенту в нужный мессенджер, например, с номера отдела продаж.
{
"account_id": "52e591f7-c98f-4255-8495-827210138c81",
"time": 1639572261,
"message": {
"receiver": {
"id": "2ed64e26-70a1-4857-8382-bb066a076219",
"phone": "+79151112233",
"email": "example.client@example.com",
"client_id":"my_int-1376265f-86df-4c49-a0c3-a4816df41af8"
},
"sender": {
"id": "76fc2bea-902f-425c-9a3d-dcdac4766090"
},
"conversation": {
"id": "8e4d4baa-9e6c-4a88-838a-5f62be227bdc",
"client_id":"my_int-d5a421f7f218"
},
"source":{
"external_id": "79039876543"
},
"timestamp": 1639572260,
"msec_timestamp": 1639572260980,
"message": {
"id": "0371a0ff-b78a-4c7b-8538-a7d547e10692",
"type": "text",
"text": "Текст сообщения Сделка #15926745",
"tag": "",
"media": "",
"thumbnail": "",
"file_name": "",
"file_size": 0
}
}
}
API Чатов вместе с API amoCRM позволяет создать новый контакт, чат и импортировать туда переписку.
API Чатов предоставляет отдельный метод создания чата, при вызове которого не будет создан новый контакт или сделка.
API amoCRM позволяет связать существующий чат с контактом по ID контакта.
С точки зрения бизнес логики, данный пример будет, когда клиент уже существующий и нам не нужно создание неразобранного.
Контакт в примере создадим для наглядности процесса.
Подробнее о методе создания чата можно прочитать в разделе API Reference.
Для начала создадим новый чат:
<?php
include __DIR__ . '/helpers.php';
// Секретный ключ канала
$channelSecret = 'f2d7f8704eff95087ed45b23ba99c0b5aac8278e';
// Scope ID, который был получен при подключении канала в аккаунт
$scopeId = '344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81';
// Тело запроса
$requestBody = [
'conversation_id' => 'my_int-8e3e7640-49af-4448-a2c6-d5a421f7f217',
'source' => [
'external_id' => '78001234567', // external_id источника в API Источников, поле не передается, если интеграция не поддерживает множественные источники
],
'user' => [
'id' => 'my_int-1376265f-86df-4c49-a0c3-a4816df41af9',
'avatar' => 'https://example.com/users/avatar.png',
'name' => 'Имя клиента',
'profile' => [
'phone' => '+79151112233',
'email' => 'example.client@example.com',
],
'profile_link' => 'https://example.com/profile/example.client',
]
];
$jsonBody = json_encode($requestBody);
$checkSum = createBodyChecksum($jsonBody);
$apiMethod = sprintf('/v2/origin/custom/%s/chats', $scopeId);
// Составим подпись
$signature = createSignature(
$channelSecret,
$checkSum,
$apiMethod
);
// Подготовим заголовки
$curlHeaders = prepareHeaderForCurl($checkSum, $signature);
echo 'POST ' . $apiMethod . PHP_EOL;
foreach ($curlHeaders as $header) {
echo $header . PHP_EOL;
}
echo PHP_EOL . $jsonBody . PHP_EOL . PHP_EOL;
// Выполним запрос
execCurl($apiMethod, $jsonBody, $curlHeaders);
POST https://amojo.amocrm.ru/v2/origin/custom/344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81/chats
Date: Thu, 16 Dec 2021 23:22:49 +0000
Content-Type: application/json
Content-MD5: 5139e573382c0eae38f476822abb2014
X-Signature: a601e486d694c8e7e8a5b637660f949f8bb7efaf
User-Agent: amoCRM-Chats-Doc-Example/1.0
{
"conversation_id": "my_int-8e3e7640-49af-4448-a2c6-d5a421f7f217",
"source": {
"external_id": "78001234567"
},
"user": {
"id": "my_int-1376265f-86df-4c49-a0c3-a4816df41af9",
"avatar": "https://example.com/users/avatar.png",
"name": "Имя клиента",
"profile": {
"phone": "+79151112233",
"email": "example.client@example.com"
},
"profile_link": "https://example.com/profile/example.client"
}
}
{
"id": "31d43a78-e09d-46ae-8994-7e93560169b8",
"user": {
"id": "7c7330cd-c0ee-45a5-bd62-643a3a8225e8",
"client_id": "my_int-1376265f-86df-4c49-a0c3-a4816df41af9",
"name": "Имя клиента",
"profile": {
"phone": "79151112233",
"email": "example.client@example.com"
},
"avatar": "https://example.com/users/avatar.png"
}
}
После создания чата, в качестве примера, создадим контакт. Также вы можете использовать уже существующий контакт.
POST /api/v4/contacts HTTP/1.1
Host: https://{subdomain}.amocrm.ru
Content-Type: application/json
Authorization: Bearer xxxx
[
{
"first_name": "Jack200803",
"last_name": "Tester"
}
]
{
"_links": {
"self": {
"href": "https://{subdomain}.amocrm.ru/api/v4/contacts"
}
},
"_embedded": {
"contacts": [
{
"id": 3102959,
"request_id": "0",
"_links": {
"self": {
"href": "https://{subdomain}.amocrm.ru/api/v4/contacts/3102959"
}
}
}
]
}
}
После создания контакта, теперь свяжем контакт и созданный чат.
POST /api/v4/contacts/chats HTTP/1.1
Host: https://{subdomain}.amocrm.ru
Content-Type: application/json
Authorization: Bearer xxxx
[
{
"contact_id": 3102959,
"chat_id": "6cbab3d5-c4c1-46ff-b710-ad59ad10805f"
}
]
{
"_total_items": 1,
"_embedded": {
"chats": [
{
"chat_id": "6cbab3d5-c4c1-46ff-b710-ad59ad10805f",
"contact_id": 3102959,
"id": 26219,
"request_id": "0"
}
]
}
}
Также можно проверить привязку чата и контакта. Для этого можно воспользоваться методом API.
Метод добавления сообщений
позволяет, кроме добавления новых сообщение от клиента, производить импорт существующих сообщений.
Импортируемые сообщения могут быть адресованы:
Метод позволяем импортировать сообщения в чат, не вызывая уведомлений для пользователей amoCRM и создания неразобранного.
Для этого необходимо передать в метод API поле payload[silent]: true
.
При массовом импорте старых сообщений в чат, рекомендуем передавать payload[silent]: true
со всеми сообщениями, кроме последнего.
В последнем сообщении (самом свежем) передаем поле payload[silent]: false
,
таким образом на последнее сообщение будет создано неразобранное и придет только одно уведомление, тем самым мы не создадим клиенту лишних беспокойств.
Также стоит отметить, что история сообщений в карточке отображается только за 24 часа до даты создания сделки/покупателя.
Подробную спецификацию и примеры можно найти в API Reference.
Еще одна возможность, которую предоставляет API Чатов – функция Написать первым.
Для получения доступа к функционалу, при создании чата, в заявке необходимо указать, что вам требуется эта функция.
Для существующих каналов её также можно включить через поддержку.
После включения функции "Написать первым", пользователи получают возможность создавать новый чат на стороне amoCRM в карточке сделки или через Salesbot.
Уникальным идентификатором для функционала является номер телефона. При создании чата, amoCRM пришлет интеграции вебхук v2.
Отличительной особенностью данного хука является отсутствие поля message[conversation][client_id],
в котором в обычных хуках приходит ID чата на стороне интеграции.
Если ваша интеграция поддерживает множественные источники, в хуке вы получите поле message[source][external_id],
в котором будет находиться переданный external_id при создании источника.
Также стоит отметить, что при добавлении нового сообщения через метод отправки сообщений,
вам необходимо передать payload[conversation_id] c ID чата на стороне интеграции, а также
payload[conversation_ref_id] с ID чата на стороне API чатов. Передача этих двух параметров позволит связать чат на стороне amoCRM с идентификатором чата на стороне интеграции.
Для связывания пользователя, которому мы пишем, с ближайшим входящим сообщением необходимо передать
поле payload[sender][ref_id] с ID пользователя на стороне API Чатов и payload[sender][id] c ID пользователя на стороне интеграции.
Некоторые мессенджеры уведомляют интеграции о том, что пользователь сейчас печатает. Интеграция может уведомить об этом amoCRM.
В таком случае в карточке, в которой отображается чат, появится анимация печати от имени пользователя мессенджера (клиента).
Важно отметить, что в запросе передается ID чата на стороне интеграции (ID переданный при отправке сообщения в payload[conversation_id]).
Также передается ID пользователя на стороне интеграции, который печатает.
Рассмотрим пример кода:
<?php
include __DIR__ . '/helpers.php';
// Секретный ключ канала
$channelSecret = 'f2d7f8704eff95087ed45b23ba99c0b5aac8278e';
// Scope ID, который был получен при подключении канала в аккаунт
$scopeId = '344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81';
// Тело запроса, отправим информацию, что пользователь печатает
$requestBody = [
'conversation_id' => 'my_int-d5a421f7f218',
'sender' => [
'id' => 'my_int-1376265f-86df-4c49-a0c3-a4816df41af8',
],
];
$jsonBody = json_encode($requestBody);
$checkSum = createBodyChecksum($jsonBody);
$apiMethod = sprintf('/v2/origin/custom/%s/typing', $scopeId);
// Составим подпись
$signature = createSignature(
$channelSecret,
$checkSum,
$apiMethod
);
// Подготовим заголовки
$curlHeaders = prepareHeaderForCurl($checkSum, $signature);
echo 'POST ' . $apiMethod . PHP_EOL;
foreach ($curlHeaders as $header) {
echo $header . PHP_EOL;
}
echo PHP_EOL . $jsonBody . PHP_EOL . PHP_EOL;
// Выполним запрос
execCurl($apiMethod, $jsonBody, $curlHeaders);
POST https://amojo.amocrm.ru/v2/origin/custom/344a5002-f8ca-454d-af3d-396180102ac7_52e591f7-c98f-4255-8495-827210138c81/typing
Date: Thu, 16 Dec 2021 16:01:21 +0000
Content-Type: application/json
Content-MD5: 255a22566d68be207fa36804e78a523a
X-Signature: e8263b1daf2b20396c02db6c8c846f95cc0f9540
User-Agent: amoCRM-Chats-Doc-Example/1.0
{
"conversation_id": "my_int-d5a421f7f218",
"sender": {
"id": "my_int-1376265f-86df-4c49-a0c3-a4816df41af8"
}
}
Мы предоставляем дополнительную возможность для интеграций – получать вебхуки, когда пользователь amoCRM начинает печатать сообщение.
Для получения подобных хуков вам необходимо обратиться в техническую поддержку или указать это при регистрации нового канала.
Запрос придет на тот же адрес, который был указан как адрес для получения Webhooks.
Мы считаем, что после каждого нажатия на клавишу, пользователь печатает еще до 5 секунд, поэтому в хуке мы присылаем timestamp-метку, показывающую, когда пользователь перестал печатать.
Вебхук отправляется не чаще, чем раз в 5 секунд.
После получения подобного хука, вам необходимо передать эту информацию в мессенджер, с которым вы интегрируетесь,
чтобы в мессенджере отобразилась информация о том, что мы сейчас печатаем.
Описание формата Webhook’ов о печати (Webhook Reference)
С помощью API чатов вы можете получить историю сообщений конкретного чата.
Для получения истории вам потребуется scope_id и ID чата на стороне API Чатов,
который можно получить в хуке о входящем сообщении или при создании чата через API.
Подробный пример доступен в cпецификации метода получения истории.
С помощью API чатов вы можете послать реакцию на сообщения или снять реакцию с сообщения спецификация метода посылки реакции.
Если интеграция сообщила список поддерживаемых реакций, то менеджеры так же смогут реагировать
на сообщения из карточки. При этом в интеграцию будет приходить хук Описание формата Webhook’ов о реакции (Webhook Reference).