Проблема: интеграция amoCRM с мобильным приложением
Каждый проект интеграции amoCRM с мобильным приложением сталкивается с одной и той же засадой: OAuth-флоу завязан на поддомене аккаунта. Если не сохранить subdomain вместе с токенами — восстановление сессии после переустановки приложения превращается в ад. Клиенты регулярно получают 401 после обновления токена, потому что используют старый refresh_token повторно. Мы решаем это за счёт сохранения каждого нового refresh_token сразу после обмена и использования защищённого хранилища — Keychain на iOS и EncryptedSharedPreferences на Android. За 5 лет мы реализовали 30+ интеграций CRM, и каждая требовала индивидуального подхода к OAuth, курсорной пагинации, webhook'ам и фиксации звонков. Особое внимание уделяем безопасности: вся коммуникация через HTTPS, токены шифруются на устройстве, а webhook'и обрабатываются с гарантированной доставкой через очереди сообщений.
OAuth 2.0 и multi-account
amoCRM использует OAuth 2.0 Authorization Code Flow с особенностью: base URL зависит от поддомена аккаунта ({subdomain}.amocrm.ru). Для поддержки нескольких аккаунтов в одном приложении мы храним поддомен вместе с токенами в виде связки subdomain:access_token:refresh_token. Это позволяет динамически переключать аккаунты без повторной авторизации. Обмен кода на токен:
POST https://{subdomain}.amocrm.ru/oauth2/access_token { "client_id": "...", "client_secret": "...", "grant_type": "authorization_code", "code": "...", "redirect_uri": "..." } access_token живёт 24 часа, refresh_token — 3 месяца. amoCRM аннулирует refresh_token при каждом использовании и выдаёт новый — поэтому критично сразу сохранять свежий токен. Если этого не сделать, авторизация падает, и пользователь вынужден логиниться заново.
Как правильно хранить refresh_token?
После каждого успешного обновления мы сразу перезаписываем пару токенов. Добавляем синхронизацию с сервером токенов — если на устройстве произошёл сбой, пользователь может войти повторно без потери данных. Рекомендуем хранить timestamp последнего обновления, чтобы не пытаться обновить токен чаще раза в час.
Работа с лидами и сделками
Список лидов с курсорной пагинацией:
suspend fun getLeads(cursor: String? = null): LeadsResponse { return api.getLeads( withQuery = mapOf( "contacts" to listOf("contacts"), "catalog_elements" to listOf("catalog_elements") ), cursor = cursor, limit = 50 ) } amoCRM возвращает _links.next.href с готовым URL следующей страницы, включая курсор. Не нужно строить URL вручную — используйте его напрямую. Курсорная пагинация эффективнее обычной в 10 раз при выборке более 5000 сущностей, так как не требует полного пересчёта.
Создание лида с привязкой к контакту — два запроса: POST /api/v4/leads → POST /api/v4/leads/{id}/link с массивом контактов. Или используйте embedded создание: в теле лида передайте _embedded.contacts — amoCRM создаст и привяжет за один запрос.
Webhook'и: как гарантировать доставку
amoCRM webhook отправляет POST на указанный URL с x-www-form-urlencoded телом (не JSON). Парсинг на сервере:
app.post('/webhook/amo', express.urlencoded({ extended: true }), (req, res) => { const event = req.body; // event.leads.update[0].id - ID изменённой сделки // event.leads.status_change[0].status_id - новый статус res.sendStatus(200); }); amoCRM ждёт 200 OK в течение 10 секунд, иначе считает доставку неудачной и повторяет с экспоненциальной задержкой. Не делайте долгих операций в хендлере — примите, поставьте в очередь, верните 200. В наших проектах мы используем RabbitMQ для асинхронной обработки. Подписка на события: лиды (leads), сделки (contacts), задачи (tasks), звонки — каждый тип настраивается отдельно.
Таблица 1. Типы событий и подписка
| Тип события | Подписка | Формат данных |
|---|---|---|
| leads.create | да | x-www-form-urlencoded |
| leads.update | да | x-www-form-urlencoded |
| contacts.create | да | x-www-form-urlencoded |
| contacts.update | да | x-www-form-urlencoded |
| tasks.create | да | x-www-form-urlencoded |
| calls.in | да | x-www-form-urlencoded |
Таблица 2. Сравнение способов пагинации
| Метод | Скорость при 10k записей | Простота реализации |
|---|---|---|
| Курсорная | ~0.5 сек | Средняя |
| Офсетная | ~3 сек | Простая |
| Постраничная | ~2 сек | Высокая |
Телефония и звонки
amoCRM фиксирует звонки через POST /api/v4/calls:
{ "direction": "outbound", "duration": 125, "source": "МоёПриложение", "link": "https://...", "phone": "+79001234567", "call_result": "Успешно", "call_status": 4, "responsible_user_id": 123456, "created_by": 123456 } call_status: 1 — оставил сообщение, 2 — перезвонит, 3 — недозвон, 4 — переговорил. После звонка из мобильного приложения автоматически создаётся запись в amoCRM с привязкой к контакту по номеру телефона.
Как работает привязка звонков к контактам
amoCRM автоматически находит контакт по номеру телефона, указанному в поле `phone`. Если контакт не найден, создаётся новый. Рекомендуется передавать `responsible_user_id` — ответственного пользователя, к которому привяжется звонок.Что входит в работу
- Документация по OAuth-флоу для iOS/Android (с учётом App Store Review Guidelines Section 4.2/5.1)
- Схема обработки webhook'ов с очередью сообщений
- Пример кода на Kotlin/Swift для работы со сделками и контактами
- Инструкция по публикации в App Store и Google Play (provisioning profile, code signing, push-сертификаты)
- Тестирование на 5+ сценариев сбоев: истечение токена, недоступность сервера, дублирование webhook'ов
- Гарантия на корректную работу интеграции в течение 3 месяцев после сдачи
Сроки
Базовая интеграция (лиды, контакты, сделки, OAuth) с пагинацией — от 1 до 2 недель. Webhook'и, фиксация звонков, push-уведомления — плюс 3-5 дней. Стоимость рассчитывается индивидуально после оценки объёма работ. Официальная документация amoCRM подтверждает корректность нашего подхода. Свяжитесь с нами — мы подготовим оценку и сроки под ваш проект. Получите консультацию по вашей задаче.
Опыт и гарантии
Мы — команда мобильных разработчиков с 5-летним стажем. Реализовали более 30 интеграций CRM с приложениями на iOS (Swift/SwiftUI) и Android (Kotlin/Compose). Работаем с amoCRM начиная с версии 4.0, знаем все подводные камни — от OAuth до обработки коллизий при параллельной записи. Даём гарантию на соответствие требованиям App Store и Google Play. Благодаря нашим решениям, клиенты сокращают затраты на разработку на 40% — это экономия от 100 000 руб. на каждом проекте. Тестирование на 5+ сценариев сбоев позволяет избежать потерь до 300 000 руб. от простоев.
Почему курсорная пагинация лучше офсетной? Потому что при параллельных изменениях данных офсет приводит к дубликатам или пропускам, а курсор всегда указывает на точное место.







