API OfferUno для CRM

API позволяет CRM-системе агентства создавать подборки, добавлять в них туры, получать заявки туристов и узнавать, что клиент открыл подборку. Все запросы и ответы — в формате JSON (UTF-8).

Адрес и ключ

https://api.offeruno.ru/v1

Ключ создаётся в кабинете агентства: Интеграции → Создать ключ (доступно владельцу и администратору). Передавайте его в заголовке каждого запроса:

Authorization: Bearer ou_live_…

Также принимается заголовок X-API-Key. Ключ даёт доступ ко всем данным агентства — храните его на сервере интеграции и не публикуйте.

Ответы и ошибки

Списки возвращаются как {"items": [...], "total": 120, "limit": 50, "offset": 0}, параметры limit (до 100) и offset. Ошибка — HTTP-код и {"error": "код", "message": "описание"}: 400 — неверные данные, 401 — нет или неверный ключ, 402 — подписка агентства закончилась (чтение доступно), 403 — нет доступа, 404 — не найдено.

Методы

GET /meАгентство, тариф и ключ — для проверки подключения
GET /managersСотрудники агентства
GET /selectionsПодборки. Фильтры: status (draft, sent, archived), client_id, manager_id, since (изменённые после даты, ISO 8601), q (поиск по названию)
POST /selectionsСоздать подборку: title, intro, template (cards, glass, list, magazine), client {name, phone, email} или client_id, manager_email или manager_id. Клиент ищется по телефону и email, если не найден — создаётся
GET /selections/{id}Подборка со всеми вариантами, фото и ссылкой для клиента (url)
PATCH /selections/{id}Изменить title, intro, status, template, клиента или менеджера
POST /selections/{id}/itemsДобавить вариант: {"item": {...}, "images": ["https://…jpg"]} — до 10 фото, сервер скачает их сам
PATCH /items/{id}Изменить вариант (любые поля из списка ниже)
DELETE /items/{id}Удалить вариант
GET /leadsЗаявки туристов. Фильтры: status (new, in_work, done, rejected), selection_id, since
PATCH /leads/{id}Сменить статус заявки: {"status": "in_work"}
GET /clientsКлиенты, поиск q по имени, телефону, email
POST /clientsСоздать клиента: name, phone, email, notes (если такой телефон или email уже есть — вернётся существующий)
GET /clients/{id}Клиент и его подборки

Поля варианта (item)

title*, kind (tour, hotel, cruise, flight, excursion, other), stars, country, region, date_from, date_to (YYYY-MM-DD), nights, nights_transit, meal, room, guests, departure, operator, price, old_price, currency (RUB, USD, EUR, KZT, BYN), price_note, flight_info, description, comment, hotel_url, source_url, recommended, tags (["hot", "deal", "rec", "value"] или свои теги агентства)

Пример: подборка для клиента из CRM

curl -X POST https://api.offeruno.ru/v1/selections \ -H "Authorization: Bearer ou_live_…" -H "Content-Type: application/json" \ -d '{"title": "Турция, июнь", "client": {"name": "Ирина Иванова", "phone": "+7 916 000-11-22"}, "manager_email": "anna@agency.ru"}' curl -X POST https://api.offeruno.ru/v1/selections/42/items \ -H "Authorization: Bearer ou_live_…" -H "Content-Type: application/json" \ -d '{"item": {"title": "Rixos Premium Belek", "stars": "5", "country": "Турция", "date_from": "2027-06-12", "date_to": "2027-06-19", "meal": "UAI", "price": 248900, "old_price": 271000, "tags": ["hot"]}, "images": ["https://example.com/rixos.jpg"]}'

В ответе на создание подборки поле selection.url — ссылка, которую CRM может отправить клиенту.

Вебхуки

В кабинете (Интеграции) укажите адрес, и OfferUno будет отправлять на него POST-запрос при событиях:

lead.createdНовая заявка туриста
selection.openedТурист впервые открыл подборку
payment.succeededТурист оплатил тур картой

Тело запроса:

{ "event": "lead.created", "created_at": "2026-10-06T09:12:00+00:00", "agency_id": 7, "delivery_id": 1532, "data": { "id": 315, "status": "new", "name": "Ирина", "phone": "+7 916 000-11-22", "email": "", "message": "Удобно звонить после 18:00", "item": {"id": 901, "title": "Rixos Premium Belek", "price": 248900, "currency": "RUB", …}, "selection": {"id": 42, "title": "Турция, июнь", "url": "https://…/p/…", "client": {…}, "manager": {…}} } }

Заголовки: X-OfferUno-Event, X-OfferUno-Delivery и X-OfferUno-Signature: sha256=… — HMAC-SHA256 тела запроса ключом подписи из кабинета. Ответьте кодом 2xx в течение 10 секунд. Если сервер недоступен или ответил ошибкой, запрос повторится через 1, 5, 30 минут, 2 и 6 часов (с тем же delivery_id — используйте его, чтобы не создать дубль).

Проверка подписи (Python):

import hmac, hashlib expected = "sha256=" + hmac.new(SECRET.encode(), request_body, hashlib.sha256).hexdigest() ok = hmac.compare_digest(expected, headers["X-OfferUno-Signature"])

PHP:

$expected = 'sha256=' . hash_hmac('sha256', file_get_contents('php://input'), $secret); $ok = hash_equals($expected, $_SERVER['HTTP_X_OFFERUNO_SIGNATURE']);