Справочник для разработчика: как получить токен, что можно читать и записывать, как
приходят ошибки. Все адреса начинаются с https://opencrm.by/api/v1.
Токен выпускает владелец компании: Настройки → Интеграции и API → API-токены. Открытое значение показывается один раз — в базе хранится только его хеш. Передавайте токен в заголовке каждого запроса.
curl https://opencrm.by/api/v1/me \
-H "Authorization: Bearer ocrm_ВАШ_ТОКЕН"
Компания определяется по токену, поэтому в адресах её нет — интеграции не нужно знать ни идентификатор компании, ни её адрес в системе.
POST и PATCH целиком.
Ошибка приходит с подходящим HTTP-кодом и телом одного формата:
{
"error": {
"code": "invalid_token",
"message": "Токен неизвестен, отозван или истёк."
}
}
| Код | HTTP | Когда приходит |
|---|---|---|
| unauthenticated | 401 | Заголовка Authorization нет или он не в формате Bearer |
| invalid_token | 401 | Токен неизвестен, отозван или у него истёк срок |
| token_owner_unavailable | 403 | Сотрудник, от имени которого выдан токен, удалён или переведён в другую компанию |
| insufficient_ability | 403 | Токен выдан только на чтение, а запрос меняет данные |
| forbidden | 403 | Сотруднику токена не хватает права; в сообщении указано, какого именно |
| module_disabled | 403 | Нужный модуль отключён в тарифе или настройках компании |
| customer_required | 422 | При создании сделки не передан ни counterparty_id, ни customer_name |
| counterparty_not_found | 422 | Контрагент с таким идентификатором в этой компании не найден |
| funnel_not_found | 422 | Воронка не найдена |
| status_not_found | 422 | Этап не найден или относится к другой воронке |
| no_funnels | 422 | В компании ещё не создано ни одной воронки |
| validation_failed | 422 | Данные не прошли проверку — подробности в сообщении |
Проверка полей встроенными правилами возвращает привычный для Laravel ответ:
message и разбор по полям в errors. Запрос к несуществующей
записи — 404, превышение лимита — 429.
Списки отдаются постранично. Параметры — page и per_page
(по умолчанию 50, максимум 200). Рядом с данными приходит блок meta.
{
"data": [ ... ],
"meta": { "page": 1, "per_page": 50, "total": 137, "last_page": 3 }
}
Чтобы забирать только изменившееся, используйте updated_since — дату
в любом понятном формате, например 2026-09-01 или
2026-09-01T10:30:00+03:00. Сделки отсортированы по времени изменения,
новые сверху.
120 запросов в минуту. При превышении приходит 429 и заголовок
Retry-After с числом секунд до следующей попытки. Даты в ответах —
в формате ISO 8601 с часовым поясом. Все суммы — числа с двумя знаками после запятой.
Первый запрос, который стоит сделать: он показывает, чей токен и что ему доступно.
{
"data": {
"company": { "id": 12, "name": "Монтаж и сервис", "slug": "montazh" },
"user": { "id": 3, "name": "Анна Ковалёва", "role": "owner" },
"token": {
"name": "Форма на сайте",
"abilities": ["read", "write"],
"expires_at": null
},
"modules": ["orders", "counterparties", "tasks", "inventory"]
}
}
Справочник, без которого нечего передавать в status_id. Этапы приходят
в порядке следования в воронке.
{
"data": [
{
"id": 1,
"name": "Продажи",
"is_default": true,
"is_active": true,
"statuses": [
{ "id": 4, "name": "Новая заявка", "type": "", "position": 1 },
{ "id": 5, "name": "В работе", "type": "", "position": 2 },
{ "id": 9, "name": "Успешно", "type": "success", "position": 8 }
]
}
]
}
Поле type отмечает закрывающие этапы: success —
успешное завершение, failure — отказ. У обычных этапов оно пустое.
Список сделок компании. Все параметры необязательны и комбинируются.
| Параметр | Что делает |
|---|---|
| funnel_id | Только сделки этой воронки |
| status_id | Только сделки на этом этапе |
| user_id | Сделки конкретного менеджера |
| counterparty_id | Сделки одного контрагента |
| updated_since | Изменённые с указанного момента — для регулярной синхронизации |
| created_since | Созданные с указанного момента |
| search | Поиск по номеру, названию, имени клиента, телефону и почте |
| page, per_page | Страница и размер страницы |
curl "https://opencrm.by/api/v1/orders?status_id=5&per_page=20" \
-H "Authorization: Bearer ocrm_ВАШ_ТОКЕН"
{
"data": [
{
"id": 812,
"number": "ORD-260906-00812",
"title": "Кондиционер, ул. Немига 5",
"funnel": { "id": 1, "name": "Продажи" },
"status": { "id": 5, "name": "В работе", "type": "" },
"manager": { "id": 3, "name": "Анна Ковалёва" },
"counterparty_id": 402,
"customer_name": "Иванов Иван",
"customer_phone": "+375 29 123-45-67",
"total_amount": 1840.00,
"source": "website",
"created_at": "2026-09-06T10:12:04+03:00",
"updated_at": "2026-09-06T11:40:19+03:00"
}
],
"meta": { "page": 1, "per_page": 20, "total": 1, "last_page": 1 }
}
Карточка сделки. К полям списка добавляются customer_kind,
customer_contact_name, customer_email,
customer_unp, customer_address, comment,
items_count и debt_amount.
Создаёт сделку. Нужен либо counterparty_id, либо
customer_name — остальное необязательно. Не указали воронку — берётся
основная, не указали этап — первый в этой воронке.
| Поле | Описание |
|---|---|
| counterparty_id | Существующий контрагент; его данные подставятся в карточку |
| customer_name | Имя клиента, если контрагента ещё нет |
| customer_phone, customer_email | Контакты клиента |
| customer_kind | individual или company; по умолчанию физлицо |
| customer_unp, customer_address, customer_contact_name | Реквизиты и контактное лицо |
| funnel_id, status_id | Куда положить сделку |
| user_id | Ответственный менеджер |
| title, comment, source | Название, комментарий и источник заявки |
| total_amount, debt_amount | Сумма сделки и долг |
| items | Позиции: product_id или product_name, quantity, price |
curl -X POST https://opencrm.by/api/v1/orders \
-H "Authorization: Bearer ocrm_ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{
"title": "Заявка с сайта",
"customer_name": "Иванов Иван",
"customer_phone": "+375 29 123-45-67",
"source": "website"
}'
Ответ — 201 и карточка созданной сделки вместе с присвоенным номером.
Позиции передаются массивом items. Товар из каталога указывается
по product_id — название и единица подставятся сами; для разовой
позиции достаточно product_name. Сумма сделки считается по позициям,
а явный total_amount её перекрывает — так удобно провести скидку.
{
"customer_name": "Иванов Иван",
"items": [
{ "product_id": 41, "quantity": 2, "price": 1200.50 },
{ "product_name": "Монтаж", "quantity": 1, "price": 300 }
]
}
Меняет только переданные поля: status_id, user_id,
title, comment, customer_phone,
customer_email, total_amount, debt_amount.
Этап должен принадлежать воронке этой сделки. Смена этапа требует права
«Сделки: смена статуса», смена менеджера — «Сделки: смена менеджера».
curl -X PATCH https://opencrm.by/api/v1/orders/812 \
-H "Authorization: Bearer ocrm_ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{"status_id": 9}'
Параметры: search (имя, компания, контактное лицо, почта, УНП),
phone, type, updated_since, а также
page и per_page.
Поиск по телефону сравнивает только цифры, поэтому формат записи не важен:
+375 (29) 765-43-21 и 297654321 найдут одного и того же
контрагента. Это ровно то, что нужно телефонии, чтобы показать карточку на входящем звонке.
curl "https://opencrm.by/api/v1/counterparties?phone=297654321" \
-H "Authorization: Bearer ocrm_ВАШ_ТОКЕН"
Карточка контрагента по идентификатору.
Обязательное поле — type: individual или
company. Физлицу нужен full_name, юрлицу —
company_name. Дополнительно принимаются contact_name,
contact_position, phone, email,
unp, address_region, address_locality,
address и comment.
curl -X POST https://opencrm.by/api/v1/counterparties \
-H "Authorization: Bearer ocrm_ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{
"type": "company",
"company_name": "ООО Ромашка",
"unp": "191234567",
"phone": "+375 17 200-30-40"
}'
Фильтры: status (planned, in_progress,
pending_approval, done), priority,
assigned_user_id, due_before, due_after,
updated_since, search, плюс page и per_page.
Карточка задачи: к полям списка добавляются описание, результат, наблюдатели, постановщик, время начала и завершения, затраченные секунды.
Обязательны title и due_at — срок в формате даты со
временем. Дополнительно: description, status,
priority, assigned_user_ids и
watcher_user_ids. Первый из ответственных становится основным,
а сотрудник, уже назначенный ответственным, в наблюдатели не дублируется.
curl -X POST https://opencrm.by/api/v1/tasks \
-H "Authorization: Bearer ocrm_ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{
"title": "Позвонить клиенту",
"due_at": "2026-09-20T10:00:00+03:00",
"assigned_user_ids": [7]
}'
Меняет только переданные поля. Перевод в done проставляет время
завершения, в in_progress — время начала. Списки участников
трогаются только если переданы: без watcher_user_ids наблюдатели
остаются как были.
Буфер входящих обращений: заявка ждёт решения менеджера и только потом становится сделкой. Внешней системе это удобнее, чем создавать сделку сразу — воронка не засоряется спамом и ошибочными обращениями.
Фильтры: status (new, accepted,
rejected), source и created_since.
Нужен хотя бы один способ связи — phone или email,
иначе придёт contact_required. Дополнительно принимаются
name, comment, page_url и произвольный
объект payload — в нём переживут любые свои поля.
curl -X POST https://opencrm.by/api/v1/leads \
-H "Authorization: Bearer ocrm_ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{
"name": "Иванов Иван",
"phone": "+375 29 123-45-67",
"comment": "Заявка с лендинга",
"payload": { "услуга": "Монтаж кондиционера" }
}'
Повторная отправка того же телефона в пределах десяти минут считается дублем и возвращает ту же заявку — двойной клик по кнопке на сайте не создаёт две.
Принять заявку: создаётся контрагент (если такого ещё нет) и сделка.
Необязательные funnel_id, status_id и
assigned_user_id говорят, куда её положить. Уже разобранная
заявка отвечает already_handled.
Товары, склады, остатки и объекты доступны только на чтение: их меняют документами внутри системы, чтобы не разошлись партии и себестоимость.
Фильтры: search (название и артикул), category_id, is_active.
Полный список складов компании — без страниц, их обычно немного.
Остатки по складам: количество, средняя цена и стоимость. Фильтры —
product_id, warehouse_id и only_in_stock.
{
"data": [
{
"product": { "id": 41, "name": "Труба медная", "sku": "TR-15", "unit": "м" },
"warehouse": { "id": 2, "name": "Основной склад" },
"quantity": 148.5,
"average_price": 12.40,
"total_cost": 1841.40
}
],
"meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}
Объекты с адресом и ответственным. Фильтры списка: search,
is_active, updated_since.
Чтобы не опрашивать API, подпишитесь на события: Настройки → Интеграции и API →
Веб-хуки. Укажите адрес получателя (только https) и нужные события —
система пришлёт секрет для проверки подписи, он показывается один раз.
| Событие | Когда приходит |
|---|---|
| order.created | Сделка создана — из карточки, через API или роботом |
| order.status_changed | Сделка перешла на другой этап; в теле есть и предыдущий этап |
| task.completed | Задачу перевели в «Выполнено» — один раз на закрытие |
| lead.created | Пришла заявка с формы на сайте или через API |
| call.finished | Звонок завершён: направление, статус, длительность, ссылка на запись |
| ping | Проверка связи по кнопке в настройках |
Мы отправляем POST с телом JSON и тремя заголовками.
POST /hooks/opencrm HTTP/1.1
Content-Type: application/json
X-OpenCRM-Event: order.status_changed
X-OpenCRM-Delivery: 4471
X-OpenCRM-Signature: sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
{
"event": "order.status_changed",
"occurred_at": "2026-09-06T12:04:11+03:00",
"data": {
"order": { "id": 812, "number": "ORD-260906-00812", "status": { "id": 9, "name": "Успешно", "type": "success" } },
"previous_status": { "id": 5, "name": "В работе", "type": "" }
}
}
Сделка внутри data.order описана так же, как в ответах API. В событии
order.created она приходит в подробном виде — с контактами, комментарием
и суммами.
X-OpenCRM-Signature — это HMAC-SHA256 от тела запроса, посчитанный вашим
секретом. Сравнивайте строки безопасным способом и считайте подпись по сырому телу,
до разбора JSON.
$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
if (! hash_equals($expected, $_SERVER['HTTP_X_OPENCRM_SIGNATURE'] ?? '')) {
http_response_code(403);
exit;
}
$event = json_decode($body, true);
Ответьте любым кодом 2xx — этого достаточно, тело ответа мы не читаем. Если получатель
ответил ошибкой или не ответил за 10 секунд, отправка повторится с растущей паузой:
через 1, 4, 9, 16 и 25 минут, всего до шести попыток. Заголовок
X-OpenCRM-Delivery у повторов тот же — по нему удобно отбрасывать дубли.
Минимальная интеграция — один запрос из обработчика формы. Контрагента можно не создавать: имя и телефон лягут прямо в сделку, менеджер свяжет её с базой при первом контакте.
<?php
// обработчик формы на вашем сайте
$response = file_get_contents('https://opencrm.by/api/v1/orders', false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Authorization: Bearer ocrm_ВАШ_ТОКЕН\r\n"
. "Content-Type: application/json\r\n",
'content' => json_encode([
'title' => 'Заявка с сайта: ' . $_POST['service'],
'customer_name' => $_POST['name'],
'customer_phone' => $_POST['phone'],
'comment' => $_POST['message'],
'source' => 'website',
], JSON_UNESCAPED_UNICODE),
'ignore_errors' => true,
],
]));
Для такой формы выпустите отдельный токен на запись и, если сайт живёт на чужом хостинге, поставьте срок действия. Токен всегда можно отозвать в настройках — остальные интеграции продолжат работать.
Внешней системе, которая забирает данные по расписанию, достаточно запоминать время
последнего обмена и запрашивать /api/v1/orders?updated_since=….
Если изменения нужны сразу, а не по расписанию, — подпишитесь на
веб-хуки.
Напишите нам: подскажем, как собрать обмен под ваш сайт, телефонию или учётную систему.