Для AI-агентов: markdown этой страницы — /docs/catalog-and-sales.mdиндекс документации — /llms.txt
Каталог и продажи
Обновлено
Каталог помогает превратить разговор с покупателем в заказ. В TelegaFirst — AI-фронт-офисе в Telegram для микробизнеса — ваш собственный агент может прочитать позиции, подготовить изменение или продажу и выполнить согласованное действие через Hub. Владелец и операторы видят диалоги в Telegram и могут перехватить разговор у AI.
Результат этого руководства: вы находите позицию своего бизнеса, меняете её со свежей версией, создаёте согласованный заказ и отдельно готовите отправку счёта. Созданный заказ ещё не означает оплату.
Что нужно до начала
Завершите подключение бизнеса, авторизацию и проверьте активный бизнес. В MCP прочитайте get_active_bot, затем обновите tools/list. В REST проверьте X-Client-Slug и X-Bot-Username ответа. API key закреплён за своим бизнесом; OAuth-подключение действует от имени человека с его текущими правами.
Чтение каталога требует catalog:read, изменение — catalog:write, ручная продажа и денежные действия — orders:write. Для REST-чтения заказов нужен pull:orders; обычный POST /api/v1/orders использует отдельное право orders. Если нужны данные покупателя, отдельно требуется pull:users. Сервер проверяет все права операции при каждом вызове; видимый инструмент не заменяет эту проверку. Для чтения данных каталога и заказов действует тарифная проверка: в пробном периоде она пройдена, после него личному агенту владельца или оператора нужно открытое платное окно любого тарифа, ключу интеграции — AI Manager или выше с открытым окном; при PLAN_UPGRADE_REQUIRED откройте тарифы, не пытайтесь заменить отказ другим ключом.
Внутренние Qualifier, Manager и Coach могут советовать и передавать разговор оператору. Денежные действия выполняет уполномоченный собственный агент клиента через Hub. Рабочее место операторов остаётся в Telegram.
Найти позицию и проверить доступность
Вызов MCP get_catalog принимает верхнеуровневые zone, limit и полученный ранее cursor:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_catalog",
"arguments": {
"zone": "ru",
"limit": 10
}
}
}REST использует GET /api/v1/catalog?zone=ru&limit=10. limit — целое число от 1 до 100, по умолчанию 50; zone — ru или com, по умолчанию ru. Обе зоны предусмотрены для бизнеса; выбор зоны влияет на представление, а русский остаётся языком этого руководства. Следующую страницу запрашивайте только с meta.next_cursor из ответа. Сохраняйте зону и условия чтения; не считайте cursor номером страницы и не конструируйте его самостоятельно.
Ниже полный пример тела успешного списка для GET /api/v1/catalog?zone=ru&cursor=Ng&limit=1 с одной позицией. Номера, даты и cursor — учебные значения из контрактного примера, а не существующие объекты вашего бизнеса. HTTP-статус — 200; тело инструмента MCP имеет ту же структуру, но не показанный здесь внешний транспортный envelope MCP.
{
"success": true,
"data": [
{
"seq_id": 7,
"title": "Course",
"title_alt": "Course EN",
"is_active": true,
"is_public": false,
"prices": [
{
"currency": "RUB",
"amount": "125.50",
"trial_amount": null
},
{
"currency": "USD",
"amount": "2.50",
"trial_amount": null
},
{
"currency": "EUR",
"amount": null,
"trial_amount": null
}
],
"available_providers": [],
"item_type": "product",
"sku_code": null,
"measurement_unit": "шт",
"requires_postal_code": false,
"updated_at": "2026-10-01T10:00:00.000Z",
"editable_config": {
"etag": "W/\"57f60dd745c6591b\"",
"editable_fields": [
"title",
"title_alt",
"description",
"mediaRefs",
"description_alt",
"is_active",
"is_public",
"prices",
"item_type",
"sku_code",
"measurement_unit",
"requires_postal_code",
"booking",
"trial",
"variants",
"ru_vat_rate",
"ru_price_includes_tax",
"intl_tax_rate_percent",
"intl_price_includes_tax"
]
}
}
],
"meta": {
"has_more": true,
"next_cursor": "Nw",
"limit": 1
}
}Доступность задаёт пара is_active / is_public: is_active:false выключает продажи; оба значения true делают позицию открытой для предложения; is_active:true вместе с is_public:false означает доступ по ссылке. Такую позицию агент не предлагает сам без соответствующего контекста. Пустой available_providers не обещает работающую онлайн-оплату: проверьте платёжную настройку.
Для detail используйте get_catalog_item с {"seq_id":7,"zone":"ru"} либо GET /api/v1/catalog/7?zone=ru. Берите номер из своего списка. seq_id — номер позиции внутри выбранного бизнеса. Внутренний ID базы данных не является адресом Hub. Ссылку для покупателя стройте только из возвращённого opaque deeplink_code и адреса своего бизнеса; не переносите seq_id в анонимный URL. Подробнее: идентификаторы и страницы списков.
Изменить позицию со свежей версией
Перед записью прочитайте detail, покажите человеку текущую позицию и согласованное изменение. Сохраните точный editable_config.etag; REST также возвращает ETag. Передавайте только разрешённые поля из editable_fields.
Для REST PATCH требуется API credential в X-Api-Key либо Authorization: Bearer tgf_*; проверка Bearer сессии на GET не делает такую сессию разрешённой для PATCH. Собственный агент через MCP действует под своим OAuth grant или другим разрешённым креденшлом и проходит те же проверки записи.
MCP-запрос для согласованного изменения названия:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "update_catalog_item",
"arguments": {
"seq_id": 7,
"zone": "ru",
"etag": "W/\"57f60dd745c6591b\"",
"idempotency_key": "catalog-change",
"patch": {
"title": "Changed"
}
}
}
}В REST это PATCH /api/v1/catalog/7?zone=ru, заголовки If-Match: W/"57f60dd745c6591b" и Idempotency-Key: catalog-change, тело:
{
"title": "Changed"
}Успех — 200, новый ETag и полный detail. Здесь приведён полный ответ для учебной позиции:
{
"seq_id": 7,
"title": "Changed",
"title_alt": "Course EN",
"description": "Frozen course",
"description_alt": "Frozen EN",
"is_active": true,
"is_public": false,
"is_free": false,
"groups": [],
"editable_config": {
"etag": "W/\"bc0efc7576cb13b2\"",
"editable_fields": [
"title",
"title_alt",
"description",
"mediaRefs",
"description_alt",
"is_active",
"is_public",
"prices",
"item_type",
"sku_code",
"measurement_unit",
"requires_postal_code",
"booking",
"trial",
"variants",
"ru_vat_rate",
"ru_price_includes_tax",
"intl_tax_rate_percent",
"intl_price_includes_tax"
]
},
"prices": [
{
"currency": "RUB",
"amount": "125.50",
"trial_amount": null
},
{
"currency": "USD",
"amount": "2.50",
"trial_amount": null
},
{
"currency": "EUR",
"amount": null,
"trial_amount": null
}
],
"item_type": "product",
"sku_code": null,
"measurement_unit": "шт",
"requires_postal_code": false,
"booking": {
"enabled": false,
"mode": "hourly",
"duration_value": null,
"buffer_minutes": 0,
"resource_seq_nums": [
12
]
},
"trial": null,
"deeplink_code": "ProductCode12345",
"ru_vat_rate": null,
"ru_price_includes_tax": null,
"intl_tax_rate_percent": null,
"intl_price_includes_tax": null,
"variants": null,
"updated_at": "2026-10-01T10:01:00.000Z"
}PATCH и его точный повтор возвращают сохранённый detail команды. Текущую доступность способов оплаты в available_providers проверяйте свежим GET /api/v1/catalog/7?zone=ru.
Для нового изменения используйте новый ключ и свежий ETag. Для повтора того же запроса после потери ответа сохраните прежний ключ и тело: каталог использует долговременную квитанцию команды. Даже прежний ETag может сопровождать точный подтверждённый повтор; это не разрешение менять тело под тем же ключом. Версии и повторы объясняют диагностику конфликта.
Продажа агентом: подготовить, показать, подтвердить
Сначала найдите покупателя и позицию в текущем бизнесе. Для самостоятельной продажи нужны user_seq_num и catalog_seq_num; для продажи по регистрации — только registration_seq_num. Эти две формы нельзя смешивать. Учебные номера ниже не нужно копировать в свой бизнес.
Вызовите
prepare_manual_sale. Подготовка не создаёт заказ.Прочитайте возвращённый
payload, проверьте выбранный бизнес, покупателя, позицию или регистрацию, а также сумму, валюту и способ оплаты. Если задаёте собственную цену, передайтеamount,currencyиproviderвместе: сумма — положительная десятичная строка с максимум двумя знаками после точки. Подготовка замораживает введённый сценарий, а проверка актуальной возможности продажи выполняется перед исполнением.Покажите человеку понятное описание: кому, что, за какую сумму и каким способом продаётся. Спросите: «Создать этот заказ на этих условиях?» Дождитесь явного подтверждения именно показанного сценария.
Вызовите
execute_manual_saleс двумя значениями из той же подготовки:preparation_idиpayload_hash. Не добавляйтеconfirmed, новую цену, tenant ID или собственный idempotency key в execute.
Пример запроса подготовки:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "prepare_manual_sale",
"arguments": {
"user_seq_num": 17,
"catalog_seq_num": 23
}
}
}В успешном теле подготовки сервер возвращает preparation_id (UUID), payload_hash (64 шестнадцатеричных символа), expires_at и полный payload. Копируйте их из фактического результата. Следующий фрагмент показывает точное построение execute-аргументов из просмотренной подготовки; reviewedPreparation — её реальное тело, а не созданный клиентом объект:
const executeArguments = {
preparation_id: reviewedPreparation.preparation_id,
payload_hash: reviewedPreparation.payload_hash
};Эти аргументы передаются инструменту execute_manual_sale только после подтверждения человека. Сервер связывает подготовку с бизнесом, видом и конкретным креденшлом, операцией и содержимым. Текущий срок подготовки — 15 минут; ориентируйтесь на возвращённый expires_at. После истечения срока или переключения бизнеса подготовьте сценарий заново и повторите человеческую проверку. Одинаковый владелец у двух подключений не делает их подготовки взаимозаменяемыми.
Полное тело результата исполнения для учебного заказа:
{
"orderSeqNum": 45,
"replayed": false
}Прочитайте созданный заказ через GET /api/v1/orders/45 с правом pull:orders; этот номер принадлежит бизнесу. Для отправки счёта нужен отдельный подтверждённый шаг из руководства по платежам. Не выполняйте mark_paid после создания заказа автоматически.
REST для уже согласованной продажи
У REST нет придуманных маршрутов подготовки: prepare_manual_sale и prepare_send_invoice — инструменты MCP. Интеграция, которая использует REST, сама показывает человеку сценарий до отправки денежной команды.
После согласования POST /api/v1/tools/sales/manual-sales с правом orders:write и Idempotency-Key: sales-manual-001 принимает одну из двух строгих форм:
{
"user_seq_num": 17,
"catalog_seq_num": 23
}Либо продажу по уже существующей регистрации:
{
"registration_seq_num": 32
}Успех — 201 с {"orderSeqNum":45,"replayed":false}. REST-форма не принимает ручную цену, price, raw ID, external_app_id или actor; собственная цена доступна в описанной MCP-подготовке. Креденшл и бизнес сервер получает из авторизации. Возможны настоящие API key, OAuth grant и M2M credential в пределах их выданных прав; создавать фиктивное приложение для агента не требуется.
Для REST денежная квитанция действует 30 дней и связывает бизнес, операцию и ключ. Точный повтор возвращает сохранённый результат с replayed:true; права и принадлежность креденшла проверяются и перед повтором. Не используйте новый ключ, пока выясняете исход операции с потерянным ответом.
Если операция отклонена
| Где | HTTP / код | Что делать |
|---|---|---|
| Авторизация REST | 401 INVALID_API_KEY | Повторить вход или использовать действующий разрешённый креденшл. |
| Права | 403 INSUFFICIENT_SCOPE | Проверить недостающее право в описании доступа. |
| Тариф чтения данных | 403 PLAN_UPGRADE_REQUIRED | Проверить доступный тариф, пробный период и оплаченное окно. |
| Detail каталога | 404 RESOURCE_NOT_FOUND | Получить актуальный seq_id из своего списка. |
| PATCH каталога без версии или ключа | 428 PRECONDITION_REQUIRED | Прочитать detail и передать оба обязательных заголовка. |
| Устаревшая версия каталога | 412 PRECONDITION_FAILED | Прочитать свежие данные, проверить изменение с человеком и взять новый ETag. |
| Строгая форма запроса | 400 VALIDATION_ERROR | Исправить указанные validation_errors, не подставлять внутренние ID. |
| Продажа: нет ключа | 400 IDEMPOTENCY_KEY_REQUIRED | Дать согласованной команде стабильный ключ. |
| Продажа: цель не найдена | 404 NOT_FOUND | Сверить номера покупателя, позиции, регистрации или заказа в текущем бизнесе. |
| Продажа: бизнес-условия неверны | 422 INVALID_REQUEST | Проверить получателя, доступность позиции и платёжные условия; не менять их молча. |
| Продажа: тот же ключ, другое содержимое | 422 IDEMPOTENCY_KEY_MISMATCH | Для нового согласованного действия создать новый ключ. |
| Продажа: квитанция ещё исполняется | 409 IDEMPOTENCY_KEY_IN_FLIGHT | Сохранить ключ, дождаться результата и повторить ту же команду. |
| Лимит ресурса | 413 PAYLOAD_TOO_LARGE или 429 RATE_LIMIT_EXCEEDED | Уменьшить запрос либо ждать Retry-After. |
Ошибки REST имеют application/problem+json, code, status, instance и traceId. Полный пример PATCH без If-Match:
{
"type": "https://telegafirst.ru/docs/errors#PRECONDITION_REQUIRED",
"title": "Precondition Required",
"status": 428,
"instance": "/api/v1/catalog/7",
"code": "PRECONDITION_REQUIRED",
"traceId": "0123456789abcdef0123456789abcdef",
"detail": "If-Match header is required for PATCH",
"hint": {
"reason": "This write is guarded by optimistic concurrency and needs the version it is based on.",
"recovery": "Read the object first and send the write with its ETag in the If-Match header."
}
}Передайте traceId поддержке при непонятном отказе. MCP-бизнес-ошибка приходит с isError:true; читайте её содержание, а не ожидайте REST-статус внутри JSON-RPC. При PREPARED_SCENARIO_TENANT_CHANGED остановите execute и проверьте бизнес. Недоступная или несовпавшая подготовка может дать безопасную внутреннюю ошибку с traceId; сервер не обещает раскрывать внутреннюю причину. Сначала проверьте исход уже начатого действия, затем подготовку и подключение. Полный справочник: ошибки.