Покупателям
Инструмент Электрика и свет Ручной инструмент Офис и дом Строительные материалы Спецодежда и СИЗ Расходные материалы Складское оборудование Крепёж Климатическое оборудование Сантехника Автосервисное оборудование Строительное оборудование Спорт и туризм Станки Всё для сада Клининговое оборудование

Public API и удалённые интерфейсы Vesremont

Операции и scopes берутся из единого реестра API. Browser WebMCP и удалённые протоколы используют отдельные модели авторизации. Исходный файл для машинного чтения: /llms/api.md.

Версия AI-документации: 1.14.0. Ревизия: sha256:a15a793af48c28fa08300228b7f7041d91b4f7c0a1e03f09eacc5ad2f68b6135.

Индекс документации: https://vesremont.com/llms.txt

Полный контракт интеграции: REST/OpenAPI, OAuth, Remote MCP, A2A, UCP, корзина, checkout, заказы, webhooks, SDK и CLI. Browser WebMCP через document.modelContext сохраняет собственный контракт. Документация описывает возможности и правила вызова; права покупателя и серверные ограничения проверяются при каждом запросе.

Интерфейсы и discovery

REST: начало работы

Публичный onboarding — чтение production-каталога без токена: GET /api/v1/products?per_page=1. По желанию получите accountless catalog credential через identity endpoint. Read-only sandbox возвращает только синтетический пример без заказов. Начинайте с опубликованных интерфейсов в agent.json; не передавайте browser cookies. При недоступном remote API используйте публичный HTML-каталог, а не внутренние AJAX-ручки.

OpenAPI 3.1 генерируется из того же реестра операций и схем, который используется обработчиками. Совместимый адрес: /.well-known/openapi.json. Таблица ниже также собрана из этого реестра. Markdown API guide доступен также по /openapi.md и /.well-known/openapi.json.md; это тот же документ /llms/api.md, не отдельная версия OpenAPI-контракта.

curl -fsS --get 'https://vesremont.com/api/v1/products' --data-urlencode 'q=труборез' --data-urlencode 'page=1' --data-urlencode 'per_page=10'

Ответы — UTF-8 JSON; POST/PATCH принимают JSON object с Content-Type: application/json, включая {} для операции без аргументов. HEAD доступен для GET. Не добавляйте завершающий / к REST-маршрутам; отдельный ранее опубликованный /api/v1/cart-drafts/ сохраняет свой URL. Неизвестные поля, дублирующиеся query-ключи и некорректные типы отклоняются.

ОперацияHTTPURLOAuth scopes
statusGET/api/v1/statusне требуется
search_productsGET/api/v1/productsне требуется
get_productGET/api/v1/products/{product_id}не требуется
search_brandsGET/api/v1/brandsне требуется
get_filtersGET/api/v1/catalog/filtersне требуется
get_reviewsGET/api/v1/products/{product_id}/reviewsне требуется
get_cartGET/api/v1/cartcart:read
add_cart_itemPOST/api/v1/cart/itemscart:write
update_cart_itemPATCH/api/v1/cart/items/{item_id}cart:write
remove_cart_itemDELETE/api/v1/cart/items/{item_id}cart:write
get_favoritesGET/api/v1/favoritesfavorites:read
add_favoritePOST/api/v1/favorites/itemsfavorites:write
remove_favoriteDELETE/api/v1/favorites/items/{product_id}favorites:write
get_checkoutGET/api/v1/checkoutcheckout:read
update_checkoutPATCH/api/v1/checkoutcheckout:write
prepare_orderPOST/api/v1/orders/prepareorder:prepare
submit_orderPOST/api/v1/orders/submitorder:submit
get_orderGET/api/v1/orders/{order_id}orders:read
list_webhooksGET/api/v1/webhookswebhooks:read
create_webhookPOST/api/v1/webhookswebhooks:write orders:read
delete_webhookDELETE/api/v1/webhooks/{subscription_id}webhooks:write
list_webhook_deliveriesGET/api/v1/webhooks/{subscription_id}/deliverieswebhooks:read

Поиск, фильтры, цена и остатки

Поиск использует существующие Manticore-индексы и нормализацию сайта; фильтры и сортировка — существующий Reindexer, подробности товара и актуальные цены — Tarantool. page начинается с 1, per_page — до 100. Поддерживаемые sort и типы фильтров описаны в OpenAPI; сначала получите /api/v1/catalog/filters с section_id или brand_id. filters передаётся одним URL-encoded JSON object, а не PHP-массивом query-параметров. Не угадывайте ID характеристик.

Cursor pagination: передавайте pagination.next_cursor следующего ответа в cursor, сохраняя запрос, фильтры и сортировку. Cursor подписан, ограничен сроком и непрозрачен: не создавайте и не редактируйте его. Не передавайте одновременно cursor и page; размер страницы уже связан с cursor. Прежняя page/per_page пагинация сохраняется для совместимости. Это последовательный обход, не гарантия неизменного снимка каталога.

Цена в REST выражена в рублях, в UCP — в целых копейках (currency: RUB). Неизвестная цена — null, не ноль. stock.total и stock.local — суммарный доступный остаток всей складской сети магазина; это не обещание самовывоза сегодня. Сроки со складов не выдумываются. updated_at: null означает отсутствие доказанного времени изменения товара. Нулевой остаток сам по себе не запрещает заказ при известной цене.

Запрос с чрезмерно широким поисковым пересечением завершается явной ошибкой: частичный результат не выдаётся за полный. Используйте более точный запрос/категорию. Отзывы отдаются только опубликованные и одобренные, без внутренних пользовательских идентификаторов и контактов.

Авторизация

Удалённый доступ отделён от browser WebMCP. Публичное чтение каталога не требует токена. Корзина, избранное, checkout и заказы требуют разрешения покупателя и Bearer access token; cookies удалённого клиента не выбирают корзину покупателя.

### Автоматический onboarding без покупателя

Начать можно сразу с GET https://vesremont.com/api/v1/products?per_page=1: ни человек, ни API key не нужны. Для изолированного знакомства с форматом есть read-only sandbox: синтетические данные, без Bitrix-корзины, покупателей, оплаты и создания заказов. Это не полный тестовый магазин.

Если клиенту нужен собственный короткоживущий credential, в AS metadata прочитайте agent_auth.identity_endpoint и identity_types_supported. Поддерживается только anonymous; внешние identity_assertion (ID-JAG), service_auth и перенос права покупателя через Claim не поддерживаются. Не отправляйте email, пароль или чужое identity assertion.

  1. Discover: PRM /.well-known/oauth-protected-resource → его authorization_servers → AS metadata → agent_auth.skill и identity_endpoint.
  2. Register: POST https://vesremont.com/oauth/identity, Content-Type: application/json, тело {"type":"anonymous"}. Публичный GET этого URL возвращает реальный шаблон запроса. Ответ POST: registration_id, подписанный identity_assertion, assertion_expires, pre_claim_scopes: ["catalog:read"].
  3. Claim: пропустить. claim_supported: false; эта регистрация никогда не превращается в покупательское разрешение.
  4. Exchange: form-urlencoded POST /oauth/token с grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer, assertion=<полученный identity_assertion>, scope=catalog:read, resource=https://vesremont.com/api/v1. Assertion одноразовый, живёт 5 минут; access token — 15 минут. Это не обмен внешнего ID-JAG и не refresh.
  5. Use: GET /api/v1/products?per_page=1 с Authorization: Bearer <access_token>. Только публичное чтение REST-каталога; токен не подходит для MCP/A2A/UCP, корзины, checkout, заказов или webhooks. Без токена публичный каталог тоже доступен.
  6. Revocation: POST /oauth/revoke с token и client_id=<registration_id>. Предъявление отозванного anonymous token получает 401; независимый публичный GET без credentials остаётся доступен.

Не сохраняйте assertion/token в URL, исходниках или логах. Повтор assertion и неправильный resource возвращают OAuth error; повышение scope запрещено. Истечение требует новой анонимной регистрации, не человека. Покупательские действия проходят отдельный CIMD/PKCE путь ниже с явным согласием и подтверждением заказа.

### Короткий путь: одно согласие для нескольких интерфейсов

Если агенту нужны REST, MCP, A2A и UCP, покупатель может разрешить их на одной странице. Это дополнительный путь; прежние восемь этапов и отдельное согласие для одного интерфейса сохраняются. Общее разрешение не является подтверждением заказа.

  1. Создайте обычный Authorization Code + PKCE S256 запрос. В resource укажите основной интерфейс, например https://vesremont.com/api/v1. В дополнительном параметре Vesremont consent_resources перечислите нужные resource URL через пробел. Список должен содержать основной resource, без повторов и посторонних адресов. Используйте URLSearchParams, не склеивайте URL вручную.
  2. В scope запросите только права, нужные задаче. Покупатель видит все интерфейсы и права и один раз нажимает «Разрешить доступ» либо «Отказать». Разрешённые scopes действуют для каждого явно перечисленного интерфейса; новых прав при обмене не появляется. UCP имеет собственные manage scopes — не заменяйте ими REST scopes и не запрашивайте их только ради публичного каталога.
  3. Обменяйте code один раз для основного resource, как описано в Exchange ниже. Для остальных одобренных интерфейсов получите отдельные Bearer tokens через тот же /oauth/token, используя ограниченный OAuth Token Exchange (RFC 8693).

Пример параметров общего согласия для чтения корзины через REST/MCP/A2A:

const params = new URLSearchParams({
  response_type: 'code', client_id, redirect_uri, state,
  code_challenge, code_challenge_method: 'S256',
  resource: 'https://vesremont.com/api/v1', scope: 'cart:read',
  consent_resources: [
    'https://vesremont.com/api/v1',
    'https://vesremont.com/mcp',
    'https://vesremont.com/a2a'
  ].join(' ')
});
const authorizationUrl = 'https://vesremont.com/oauth/authorize?' + params;

Для UCP добавьте https://vesremont.com/ucp/v1 в тот же список и только необходимые UCP scopes. После первичного обмена отправьте form-urlencoded POST:

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token_type=urn:ietf:params:oauth:token-type:access_token
subject_token=<исходный access_token основного интерфейса>
client_id=<тот же client_id>
resource=https://vesremont.com/mcp
scope=cart:read

scope можно только сузить; без него сохраняются scopes исходного токена. Ответ содержит access_token, issued_token_type, token_type, expires_in, scope. Каждый выданный токен имеет одну аудиторию: REST token по-прежнему не принимается MCP/A2A/UCP. Полученные таким обменом токены нельзя обменивать дальше. Прежнее разрешение без списка дополнительных интерфейсов не даёт доступа к ним.

Обмен не продлевает срок: новый токен истекает не позже исходного токена и разрешения. Refresh tokens не выдаются. Отзыв исходного токена прекращает использование всех его производных; отзыв одного производного прекращает только его доступ. Отзыв общего разрешения покупателем закрывает все связанные интерфейсы. Для продолжения после истечения снова требуется согласие покупателя.

Дальнейшая подготовка выполняется агентом автоматически в пределах разрешения. Конкретный заказ покупатель отдельно подтверждает на сайте по реальной сводке товаров, суммы, получения, оплаты и контактов. CSRF/Origin, живая привязка к покупателю, ownership и Idempotency-Key остаются обязательными.

### Discover

Шаг 1 — обнаружить авторизацию.

Прочитайте OAuth metadata и OpenAPI. Их Markdown-представления: OAuth walkthrough и API guide. Это инструкции, не копии машинных схем. Документация описывает реализованный контракт; доступность удалённых интерфейсов определяется live discovery, а не наличием этой инструкции.

### Pick method

Шаг 2 — выбрать способ доступа.

Публичное чтение каталога не требует регистрации или токена. Личные операции используют OAuth Authorization Code, PKCE S256 и обязательный resource. Password grant, client credentials, refresh token и OIDC не поддерживаются. Browser WebMCP использует браузерную сессию и CSRF, а не REST Bearer token.

### Register

Шаг 3 — представить клиент.

Опубликуйте свой HTTPS Client ID Metadata Document (CIMD). Его URL — client_id. Динамического registration endpoint нет; не отправляйте запрос на выдуманный /register. Преднастроенный native CLI описан отдельно ниже.

Пример CIMD — замените домен и callback собственными HTTPS-адресами:

{
  "client_id": "https://your-app.example/oauth/client.json",
  "client_name": "Your application",
  "redirect_uris": ["https://your-app.example/oauth/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

### Claim

Шаг 4 — идентифицировать приложение, не покупателя.

Отдельного claim endpoint или API key для доступа к покупателю нет; анонимный ключ каталога не заменяет его согласие. Сервер получает CIMD по HTTPS: client_id должен совпасть с URL документа, redirect_uri — точно с одним из опубликованных URI. CIMD — метаданные public client, не секрет и не аутентификация покупателя. PKCE связывает обмен кода с исходным verifier; сам по себе он не удостоверяет владельца домена или личность покупателя. Доступ к покупателю даёт только его согласие на Vesremont.

### Authorize

Шаг 5 — получить согласие покупателя.

Сгенерируйте случайные PKCE verifier и state; храните их на стороне клиента. code_challenge = BASE64URL(SHA256(verifier)), без padding. Откройте покупателю /oauth/authorize?response_type=code&client_id=...&redirect_uri=...&scope=...&resource=...&state=...&code_challenge=...&code_challenge_method=S256. Кодируйте каждое значение параметра отдельно.

Покупатель на Vesremont видит приложение, запрашиваемые права и получателя токена и сам разрешает доступ. Предварительная регистрация для гостевой корзины не обязательна. Вернувшись в callback, проверьте точное совпадение state и ожидаемый issuer, если он передан. При отказе не продолжайте личные операции.

### Exchange

Шаг 6 — обменять одноразовый код.

В течение 5 минут отправьте form-urlencoded POST на /oauth/token: grant_type=authorization_code, code, исходные client_id, redirect_uri, resource, code_verifier. Код одноразовый. Токен живёт до 15 минут, исходное разрешение — до 1 часа; ориентируйтесь на expires_in, а не на собственные предположения. Никогда не публикуйте verifier, code или token.

### Use

Шаг 7 — выполнить разрешённую операцию.

Передавайте токен только в Authorization: Bearer … по HTTPS. Не помещайте его в URL, логи или ссылки для покупателя. При истечении снова получите согласие; автоматического refresh нет. После неопределённого исхода записи перечитайте состояние, не повторяйте заказ с новым ключом.

resource выбирается отдельно для токена: https://vesremont.com/api/v1, https://vesremont.com/mcp, https://vesremont.com/a2a либо https://vesremont.com/ucp/v1. Используйте только опубликованные интерфейсы. Токен одного получателя не подходит другому; общее согласие позволяет получить отдельные токены описанным выше обменом. Запрашивайте минимальные scopes из OpenAPI/описания инструмента; UCP использует dev.ucp.shopping.cart:manage и dev.ucp.shopping.checkout:manage.

### Revocation

Шаг 8 — отозвать доступ.

Form-urlencoded POST /oauth/revoke с token и client_id. Отзыв исходного токена прекращает доступ и его производных. Покупатель может отозвать всё разрешение и связанные уведомления на странице разрешений. Выход из аккаунта, смена пользователя или потеря исходной браузерной сессии прекращают доступ к её данным. Клиент должен удалить отозванные токены; успешный revoke не разрешает дальнейшие операции.

### Errors

OAuth возвращает поле error; REST использует application/problem+json и code. Это разные контракты ошибок: не считайте любой HTTP 200 доказательством полученных прав. Проверяйте HTTP status, формат и обязательные поля ответа.

  • При отказе покупателя (access_denied) остановите flow; новый запрос согласия возможен только по его действию.
  • При неверном state, issuer или неожиданном callback отклоните ответ. Не обменивайте код и не продолжайте личные операции.
  • При invalid_grant не повторяйте обмен одноразового кода: он мог истечь, быть использован или не соответствовать PKCE/resource. Начните новое разрешение покупателя.
  • При REST 401 прекратите использование токена; при необходимости получите новое разрешение. При 403 проверьте scopes и resource, не подменяйте пользователя или cookies.
  • При 429 соблюдайте Retry-After, если он передан. При сетевом сбое или 503 не считайте запись успешной: перечитайте состояние перед повтором; для заказа сохраняйте исходный Idempotency-Key.

Для поддержки сохраняйте только безопасный request_id, если он есть. Не записывайте authorization code, verifier, токены, полный callback URL, cookies или контакты покупателя в общие логи.

### Native CLI

Преднастроенный public client vesremont-cli использует loopback callback http://127.0.0.1:{port}/callback только на компьютере покупателя. Самостоятельному приложению не следует выдавать себя за этот клиент: HTTPS-интеграция использует собственный CIMD. CLI открывает Authorization Code + PKCE flow, проверяет state, обменивает код и отзывает токен при завершении команды. Встроенный CLI использует порт 8765; он должен быть свободен. HTTP допускается только для этого loopback callback, а API и OAuth endpoints остаются HTTPS.

### Browser consent и CSRF

Согласие на доступ, подтверждение заказа и UCP review обрабатывает сайт в текущем браузерном контексте. Страница публикует CSRF-токен, JavaScript передаёт его в изменяющем POST, сервер выполняет действующую проверку. PKCE и state не заменяют эту защиту. Внешний агент открывает страницу покупателю и не отправляет согласие или подтверждение вместо него; при отказе не продолжает личные операции.

Origin браузерного приложения должен быть явно разрешён оператором для CORS. CORS не заменяет OAuth. Серверный клиент не должен подделывать browser Origin. Наличие UCP profile, Agent Card или MCP descriptor также не даёт прав на покупателя.

Ограничения и ошибки

REST-ошибки имеют application/problem+json, HTTP status, code и request_id, общий typed schema в OpenAPI. Сохраняйте только безопасный request ID для обращения в поддержку. 400 — исправьте параметры; 401 — повторная авторизация; 403 — недостаточно прав; 404 — отсутствующий или чужой ресурс; 409 — конфликт/устаревшее состояние; 422 — нарушение схемы аргументов или недействительный cursor; 429 — соблюдайте Retry-After; 503 — функция отключена или зависимость недоступна. Ответ status: ok проверяет API/PHP, а не здоровье всех хранилищ.

Лимиты по минутным окнам: catalog/read — 120, search — 30, mutations — 60, submit/webhook — 10, OAuth — 20. Дополнительно действует общий nginx-лимит. RateLimit-Policy содержит имя/q/w, RateLimit — имя/r/t по IETF draft-ietf-httpapi-ratelimit-headers-11 (ещё draft, не окончательный RFC). Совместимые RateLimit-Limit/Remaining/Reset сохранены; Reset — относительные секунды. Legacy X-RateLimit-Reset — Unix timestamp. На 429 есть Retry-After; раннее отклонение до policy может не содержать других заголовков. Не обходите лимит сменой идентичности. Личные ответы не предназначены для общего кэша; не логируйте credentials.

Совместимость и вывод из эксплуатации: политика опубликована в x-deprecation-policy OpenAPI, минимальное предупреждение — 180 дней. Сейчас вывод операций не объявлен. Действующие маршруты не помечаются устаревшими без реального объявления; при объявленном выводе используются Deprecation, Sunset и ссылка на замену. Общее согласие добавлено совместимо, отдельные согласия не удалены.

Ссылка на готовую корзину без OAuth

Вызовите GET /api/v1/cart-drafts/?product_id=375094&quantity=1. Ответ содержит share_url, draft_id, expires_at, items. Для следующего товара извлеките параметр draft из выданного share_url и передайте его вместе с очередными product_id/quantity; draft_id для этого не подходит. Передайте покупателю именно share_url из ответа, либо используйте /buy/375094:1 для короткой ссылки. Обращение к этим адресам не создаёт заказ и не даёт доступа к чужой корзине.

В share_url находится подписанный HMAC-SHA256 token, а не хеш браузерной сессии и не OAuth token. Он содержит только ID товаров, количества и срок: персональных данных и цены в нём нет. Подпись предотвращает подмену, но не скрывает состав. API-черновик живёт до 24 часов, permalink создаёт черновик на 1 час. Доступны до 20 разных товаров, количества 1–99. Не рассчитывайте подпись на клиенте; серверный ключ не публикуется.

GET страницы handoff сам по себе не изменяет корзину: перенос идёт защищённым browser POST в собственную сессию покупателя. При обычном пользовательском переходе он автоматизирован, при отсутствии подтверждённой навигации показывается кнопка. Сервер заново проверяет товары и цены, сохраняет остальные позиции покупателя; повторный перенос не увеличивает количество уже перенесённых позиций. Неверная или истёкшая подпись не допускает перенос. После переноса обычная кнопка корзины называется «Оформить».

Корзина и checkout

API использует настоящую Bitrix/FUSER-корзину разрешившего доступ покупателя. POST /cart/items принимает product_id и количество, а PATCH/DELETE используют item_id строки корзины, не ID товара. Уточняйте поля и границы в OpenAPI. Каталожная цена всегда берётся сервером. Избранное и checkout используют ту же сессию, что сайт; после изменений перечитайте состояние. Не передавайте придуманные delivery/payment ID: выберите опубликованные для текущей корзины варианты get_checkout.

Не повторяйте неидемпотентное добавление в корзину вслепую после сетевого сбоя — сначала перечитайте корзину. Поддержка Idempotency-Key для отправки заказа не означает идемпотентность всех REST POST.

Заказ: обязательное подтверждение покупателя

  1. Заполните корзину и checkout. Вызовите POST /api/v1/orders/prepare с {} и scope order:prepare.
  2. Передайте покупателю выданный confirmation_url: полную сводку и кнопку подтверждения показывает сам Vesremont в исходной браузерной сессии. Текстовое «да» в чате не заменяет этот серверный шаг. Агент не может одобрить заказ своим Bearer token.
  3. После подтверждения вызовите /api/v1/orders/submit со scope order:submit, неизменным confirmation token и новым случайным Idempotency-Key. Точное тело указано в OpenAPI. Один подготовленный состав — один ключ.
  4. Повтор запроса допустим только с тем же ключом и тем же телом. При 202/неопределённом исходе не создавайте новую отправку. Сервер сверяет устойчивый маркер заказа; при необходимости требуется операторская проверка.
  5. Изменение корзины/контактов/доставки/оплаты делает прежнее подтверждение недействительным. Повторите prepare, просмотр и подтверждение.

Сохранённый заказ означает отправленную заявку, не автоматическое согласование цены, доставки или оплаты магазином. API не вызывает платёжный шлюз и не принимает данные банковских карт. Чтение /orders/{order_id} ограничено заказами этой пары покупатель–приложение.

Remote MCP

/mcp использует Streamable HTTP версии 2026-07-28, официальный SDK и единый реестр бизнес-операций. Получите перечень через tools/list и схемы inputSchema/outputSchema; он не равен списку 26 browser WebMCP tools. Публичные чтения анонимны, личные tools проверяют отдельный OAuth resource https://vesremont.com/mcp и scopes. Используйте совместимый MCP-клиент, обязательные protocol/method headers и JSON/SSE Accept, а не внутренний browser AJAX.

В MCP Idempotency-Key передаётся аргументом idempotency_key инструмента отправки заказа. Resources содержат только локальную публичную документацию; произвольные URL не загружаются. Наличие tools в списке не заменяет OAuth и покупательское подтверждение.

Последовательность клиента: initialize → notifications/initialized → tools/list или resources/list → вызовы. Сохраняйте выданный Mcp-Session-Id и согласованный Mcp-Protocol-Version; по завершении закройте MCP-сессию DELETE. Session ID не является OAuth-разрешением. Не подделывайте Origin: неизвестный браузерный Origin отклоняется.

resources/list/resources/read возвращают публичные документы и схему каталога с MIME и непустым содержимым. MCP App ui://vesremont/catalog публикуется как text/html;profile=mcp-app; инструменты поиска/товара связывают её через _meta.ui.resourceUri. Это отображение результата в sandbox совместимого клиента, не обход авторизации и не автоматическая покупка. Публичный view — https://vesremont.com/mcp/view: HTTP CSP разрешает embedding только ChatGPT/Claude, connect и внешние JS — только vesremont.com; формы, дочерние frames и inline scripts запрещены. _meta.ui.csp ограничивает host sandbox теми же публичными ресурсами; сама meta CSP не обеспечивает frame-ancestors.

A2A

Agent Card: /.well-known/agent-card.json. Реализован A2A 1.0 HTTP+JSON, POST /a2a/message:send. Передавайте message с уникальным messageId, role: "ROLE_USER" и одной частью parts: {"data":{"operation":"search_products","arguments":{"q":"труборез"}}}. Текстовая часть интерпретируется как поисковый запрос, не как произвольная команда. buyer_guidance возвращает действующие покупательские условия.

Ответ — синхронный Message. Background Tasks, streaming, task push notifications и выдуманный task lifecycle не объявляются. Для личных операций нужны те же права с resource https://vesremont.com/a2a; prepare_order возвращает ссылку покупательского подтверждения, а не обходит его.

UCP 2026-08-25

Business profile: /.well-known/ucp; транспорт REST /ucp/v1. Используйте TLS 1.3, UCP-Agent: profile="https://<ваш-публичный-host>/.well-known/ucp" и уникальный Request-Id. Для POST/PUT обязательны JSON Content-Type и Content-Digest: sha-256=:BASE64(SHA256(raw_body)): по фактически отправляемым байтам. Profile должен соответствовать официальной platform schema и объявлять запрашиваемые capabilities; сервер не выполняет запросы по произвольным URL из profile. Доступность capability определяется пересечением профилей.

Анонимные catalog operations: POST /catalog/search, POST /catalog/lookup, POST /catalog/product относительно /ucp/v1. Параметры и ответы соответствуют схемам UCP указанной версии; product ID передаётся в JSON и является числовым ID Vesremont. Search cursor непрозрачный: не вычисляйте его самостоятельно.

Permalink /buy/{id}:{qty},{id}:{qty} передаёт до 20 товаров (1–99 каждого) в существующий подписанный cart-draft handoff. Link не содержит цены, пользовательские/платёжные данные не импортируются. Поддерживается raw ID или base64url ID с префиксом ~. checkout.destination ограничен локальными публичными страницами Vesremont. Открытие ссылки не создаёт заказ; браузер защищённым POST переносит товары в реальную корзину.

Cart: POST /carts, GET/PUT /carts/{id}, POST /carts/{id}/cancel; OAuth scope dev.ucp.shopping.cart:manage, resource https://vesremont.com/ucp/v1. Это та же настоящая корзина покупателя. Create/PUT используют полную замену line_items, а не добавление дельты; cancel очищает корзину. Все изменяющие UCP-запросы требуют стабильного Idempotency-Key (16–128 символов). Удалённые price/totals отклоняются; товарные ID и количества проверяются по текущему каталогу.

Checkout: POST /checkout-sessions, GET/PUT /checkout-sessions/{id}, POST /{id}/complete и /{id}/cancel внутри /checkout-sessions. Scope dev.ucp.shopping.checkout:manage; для cart_id conversion нужны также cart scope и согласованная cart capability. Переданный cart_id использует текущий состав корзины; пересекающие его line_items/buyer hints не переопределяют состав.

Удалённый complete возвращает requires_escalation и continue_url: покупатель на Vesremont проверяет состав, контакты, реальную сводку и сам отправляет заказ. До этого удалённый запрос заказ не создаёт. Статус completed и order ID появляются только после успешного сохранения в Bitrix; неопределённый результат не считается завершением и не приводит к повторному Save. UCP payment handlers не заявлены: сайта достаточно для выбора доступного способа оплаты, но агент не передаёт card/payment credentials.

Уведомления о заказах

REST subscriptions требуют webhooks:read/webhooks:write, создание — дополнительно orders:read. HTTPS receiver сначала получает подписанный challenge и должен вернуть его; до успешной проверки события не отправляются. Секрет выдаётся один раз в ответе создания и хранится клиентом безопасно.

Standard Webhooks: проверяйте webhook-id, webhook-timestamp, webhook-signature по raw body и HMAC-SHA256; допускайте ограниченное окно времени и дедуплицируйте по event ID. У signing_secret удалите префикс whsec_ и декодируйте base64: ключ — полученные 32 байта. Формат подписи — v1,<base64>, подписываемая строка — <id>.<timestamp>.<raw body>. Не пересериализуйте JSON перед проверкой. При событии webhook.verify ответьте JSON {"challenge":"<значение payload.challenge>"}; для обычных событий — 2xx после устойчивого приёма. Обработку делайте идемпотентной.

Реальные события: order.status_changed, order.cancelled, payment.status_changed; контакты покупателя в payload не входят. Worker наблюдает сохранённые состояния, поэтому не обещает каждое промежуточное изменение. Повторы ограничены; история попыток доступна через deliveries. По умолчанию подписка ограничена сроком разрешения. Более долгий срок возможен только при отдельно включённой политике и явном согласии покупателя; всегда требуется живая исходная сессия. Отзыв прекращает будущие доставки, но не отменяет уже начавшийся HTTP-запрос.

Доставку выполняет штатный worker по cron, а не открытая страница покупателя. Во время challenge/retry покупателю ничего нажимать не требуется. Изменение статуса заказа оператором — отдельное действие только для проверки соответствующего события. После удаления подписки новые события по ней не доставляются. Не создавайте новый заказ ради ожидания webhook.

SDK и CLI

Компактные клиенты версии 0.2.0, лицензия MIT. Runtime-зависимостей нет. Это реальные установочные артефакты; npm, PyPI, RubyGems и Go proxy пока не являются каналами публикации. Клиенты используют существующий REST API и не включают отключённые функции.

npm install https://vesremont.com/developers/downloads/vesremont-api-0.2.0.tgz
npm install -g https://vesremont.com/developers/downloads/vesremont-api-0.2.0.tgz
python -m pip install https://vesremont.com/developers/downloads/vesremont_api-0.2.0-py3-none-any.whl
vesremont --help

Node SDK: ESM и TypeScript declarations. Python wheel устанавливается без компиляции; setuptools 77+ нужен только для сборки sdist. Ruby: скачайте .gem, затем gem install ./vesremont-api-0.2.0.gem --local. Go: распакуйте исходный модуль и используйте go mod edit -require=vesremont.com/sdk/go@v0.2.0 -replace=vesremont.com/sdk/go=./vesremont-go-0.2.0; публичный vanity import пока не настроен, go get не обещается. Точные инструкции находятся в README внутри соответствующего пакета.

SDK предоставляют status, поиск, товар, чтение корзины, cart-draft и общий request для остальных опубликованных REST-операций. Точные примеры — в README каждого пакета. CLI: status, search, product, cart-link, cart, request. cart --login использует текущий OAuth PKCE, scope cart:read и loopback callback на внешнем ПК; токен хранится в памяти одной команды и затем отзывается. Для собственных последовательностей используйте ранее полученный REST Bearer token (CLI: VESREMONT_ACCESS_TOKEN). Python SDK принимает токен, но не дублирует интерактивный мастер OAuth.

import { VesremontClient } from '@vesremont/api';
const api = new VesremontClient();
const products = await api.search('труборез', { page: 1, per_page: 10 });
const product = await api.product(375094);
const draft = await api.cartDraft(375094, 1);
// Передайте draft.share_url покупателю; не публикуйте его в общих логах.
from vesremont_api import VesremontClient
api = VesremontClient()
products = api.search('труборез', page=1, per_page=10)
product = api.product(375094)
draft = api.cart_draft(375094, 1)
vesremont search "труборез"
vesremont product 375094
vesremont cart-link 375094 1
vesremont cart --login

Общий метод SDK request принимает путь относительно /api/v1. JS: api.request('GET', '/catalog/filters', {query: {section_id: 7531}}); Python: api.request('GET', '/catalog/filters', query={'section_id': 7531}). Для связанных личных запросов создайте JS-клиент с {token} или Python-клиент с token=...; токен должен принадлежать REST resource и иметь необходимые scopes. Получайте credentials из защищённой среды, не из исходника.

Нет автоматических повторов запросов и переходов по redirect. Записи CLI требуют --write; submit требует стабильного Idempotency-Key, prepare и подтверждения покупателя на сайте. Использовать с внешнего ПК/хостинга, не с сервера Vesremont. CLI работает с production; синтетический read-only пример доступен отдельно по /oauth/sandbox, не для пробных заказов. Для подключения MCP используйте прямой endpoint и его discovery descriptor; установка SDK не требует регистрации в MCP Registry.

Границы контракта

Покупательские вызовы относятся к настоящему магазину. Только /oauth/sandbox — отдельный синтетический read-only пример; тестовых заказов он не создаёт. REST и удалённые протоколы не предоставляют доступ к произвольным файлам, SQL, внутреннему AJAX или запуску кода. Для MCP используйте discovery descriptor и прямое подключение. Отзывы можно читать, но публичный REST не предоставляет отправку отзывов или медиа. Вопросы безопасности: security.txt.