Car2B

Для разработчиков · v1

Car2B Remote Sales API

Публичный API «Удалённого отдела продаж»: ваша система сама ведёт программу, принимает заявки, берёт сток, бронирует автомобили и фиксирует сделки — всё то, что доступно в кабинете.

Базовый адрес: https://car2b.ru/api/v1/remote-sales Формат: JSON, UTF-8 Ошибки: application/problem+json
Содержание

Для кого и как это работает

Car2B — площадка: сторона сделки и расчётов — сами участники. API рассчитан на две роли, у каждой свой префикс пути и свои ключи.

РольКто этоЧто делает через APIПрефикс
Организатор программы владелец стока ведёт программу и уровни, настраивает сток, приглашает организации и принимает заявки, видит брони, подтверждает сделки по своим автомобилям /owner/*
Агент удалённый ОП ищет программу в каталоге, подаёт заявку, синхронизирует доступный сток, бронирует, фиксирует покупки /agent/*
  • Программа адресуется по program_guid во всех ручках — не полагайтесь на то, что у организации ровно одна программа.
  • Доступ = скоуп и участие. Организатор работает только со своими программами, агент — только внутри active-партнёрства (каталог и чтение собственных партнёрств, в том числе ожидающих и отклонённых, доступны и без него). Чужой ресурс неотличим от несуществующего: 404 not_found.
  • Партнёрство со статусом suspended приостановлено модерацией площадки: чтение доступно, брони и сделки — нет.
  • Ресурсы и действия, созданные через API, помечаются source: api (из интерфейса — ui).

Доступ

Ключи, назначения, обмен на токен, разрешённые IP и подпись вебхуков одинаковы для всех API Car2B и описаны в общем разделе «Ключи и доступ». Здесь — только то, что относится к Удалённому ОПу.

  • Ключ создаётся в личном кабинете: Профиль → Интеграции. Один ключ — одно назначение: «Удалённый ОП — организатор программы» (скоупы remote-sales:owner.read remote-sales:owner.write) или «Удалённый ОП — агент» (remote-sales:agent.read remote-sales:agent.write). Скоуп write не включает read. Нужны обе роли — выпустите два ключа.
  • Лимит — 5 активных ключей на организацию для каждого назначения.
  • Действия совершаются от имени владельца ключа. У него должен быть доступ к разделу «Удалённый ОП» (иначе 403 permission_missing), организация должна быть верифицирована (403 organization_unverified), а при включённой платной модели — с активной подпиской (403 subscription_required): те же правила, что в кабинете. Право проверяется на каждом запросе.
  • Если API временно выключен на стороне площадки, ответ — 503 feature_disabled с заголовком Retry-After.
curl -X POST https://car2b.ru/api/v1/identity/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "grant_type=client_credentials"
# → { "access_token": "…", "token_type": "Bearer", "expires_in": 300, "scope": "…" }

Быстрый старт: организатор

Ключ с назначением «Удалённый ОП — организатор программы». Список ваших программ и их program_guid:

curl https://car2b.ru/api/v1/remote-sales/owner/programs \
  -H "Authorization: Bearer $TOKEN"

Опубликовать программу и выбрать, какой сток доступен агентам:

# опубликовать программу в каталоге (повтор на опубликованной — 200 без эффекта)
curl -X POST https://car2b.ru/api/v1/remote-sales/owner/programs/$PROGRAM_GUID/publish \
  -H "Authorization: Bearer $TOKEN"

# какие автомобили доступны агентам: all | include | exclude
curl -X PUT https://car2b.ru/api/v1/remote-sales/owner/programs/$PROGRAM_GUID/stock-settings \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "mode": "exclude", "vehicle_guids": ["6d1f2c8e-9b7a-4c1e-8f3d-2a5b6c7d8e9f"] }'

Пригласить организацию и принять заявку агента:

# пригласить организацию в программу (Idempotency-Key необязателен, но рекомендуется)
curl -X POST https://car2b.ru/api/v1/remote-sales/owner/partnerships \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: invite-2026-10-13-001" \
  -H "Content-Type: application/json" \
  -d '{ "program_guid": "…", "remote_org_guid": "…", "message": "Приглашаем в программу" }'

# принять заявку агента
curl -X POST https://car2b.ru/api/v1/remote-sales/owner/partnerships/$PARTNERSHIP_GUID/accept \
  -H "Authorization: Bearer $TOKEN"

Приглашение неизвестной организации возвращает 422 unknown_organization; идентификатор передаётся в теле запроса, а не в пути.

Быстрый старт: агент

Ключ с назначением «Удалённый ОП — агент». Найти программу в каталоге и подать заявку:

curl "https://car2b.ru/api/v1/remote-sales/agent/catalog/programs?q=УАЗ&limit=20" \
  -H "Authorization: Bearer $TOKEN"

# заявка на программу (Idempotency-Key необязателен)
curl -X POST https://car2b.ru/api/v1/remote-sales/agent/partnerships \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "program_guid": "…", "message": "Хотим работать по вашей программе" }'

Пока организатор не принял заявку, партнёрство в статусе pending_request; статус и приход partnership.accepted можно отслеживать через GET /agent/partnerships или вебхук. После перехода в active доступен сток:

# полный обход стока активного партнёрства
curl "https://car2b.ru/api/v1/remote-sales/agent/partnerships/$PARTNERSHIP_GUID/stock?limit=200" \
  -H "Authorization: Bearer $TOKEN"

# → { "items": [ { "vehicle_guid": "…", "removed": false, "price": 1990000, … } ],
#     "next_cursor": "",
#     "sync_token": "…" }   # sync_token — только на последней странице

Бронь и сделка:

# бронь на 2 часа; Idempotency-Key обязателен
curl -X POST https://car2b.ru/api/v1/remote-sales/agent/reservations \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: res-7f0d3c1a" \
  -H "Content-Type: application/json" \
  -d '{ "partnership_guid": "…", "vehicle_guid": "…" }'

# зафиксировать покупку; Idempotency-Key обязателен
curl -X POST https://car2b.ru/api/v1/remote-sales/agent/deals \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: deal-7f0d3c1a" \
  -H "Content-Type: application/json" \
  -d '{ "partnership_guid": "…", "vehicle_guid": "…", "price": 1890000, "currency": "RUB", "reservation_guid": "…" }'

Основные сценарии

Организатор

  1. Подготовить программу. PATCH /owner/programs/{program_guid} (карточка), PUT …/levels (полная замена набора уровней; уровень накопительный, прошлые сделки не пересчитываются), PUT …/stock-settings (режим all, include или exclude со списком vehicle_guids — объявления вашей организации).
  2. Опубликовать. POST …/publish: draft или unpublished → published; снять с каталога — POST …/unpublish. Заблокированную администрацией программу менять нельзя: 409 program_blocked.
  3. Принимать заявки. GET /owner/partnerships?status=pending_request (или вебхук partnership.requested), затем POST /owner/partnerships/{partnership_guid}/accept или …/decline. Активное партнёрство завершается через …/terminate.
  4. Брони. GET /owner/reservations, GET …/{reservation_guid}, снять бронь — POST …/cancel.
  5. Подтверждать сделки. GET /owner/deals, затем POST /owner/deals/{deal_guid}/confirm; до завершения сделку можно отменить (…/cancel), при расхождении — …/dispute.

Агент

  1. Найти программу. GET /agent/catalog/programs с фильтрами (q, city, region, brand, min_vehicles, reservation_enabled), карточка — GET …/{program_guid}.
  2. Подать заявку. POST /agent/partnerships с program_guid. Возможные 409: already_exists, geography_restricted, self_partnership. Если организатор сам пригласил вас — статус pending_invite, пришёл вебхук partnership.invited.
  3. Синхронизировать сток активного партнёрства — см. следующий раздел.
  4. Забронировать. POST /agent/reservations — бесплатно, до 2 часов. Действуют правила домена, как в кабинете: брони включены у программы и не отключены партнёру, у организации не больше одной активной брони на платформе и не больше одной брони в сутки на владельца, автомобиль доступен. Нарушение — 409 с кодом из таблицы ошибок. Снять — POST …/cancel.
  5. Зафиксировать сделку. POST /agent/deals (цена договора купли-продажи между сторонами, опционально reservation_guid — тогда бронь получит статус converted_to_deal). Инициатор считается подтвердившим; сделка завершается после подтверждения продавцом. Дальше …/confirm, …/cancel, …/dispute.

Синхронизация стока

GET /agent/partnerships/{partnership_guid}/stock отдаёт автомобили владельца в рамках режима стока программы, только для active-партнёрства (иначе 409 partnership_not_active). Розничную цену задаёт владелец. Фильтры brand и model — по живым позициям.

  1. Первый обход — полный. Без updated_since, постранично по next_cursor (пустой — последняя страница). Порядок — по (updated, vehicle_guid).
  2. Сохраните sync_token с последней страницы обхода.
  3. Следующие обходы — инкрементальные: передайте токен как updated_since и в конце снова сохраните новый sync_token.
# следующие обходы — только изменения с прошлого раза
curl "https://car2b.ru/api/v1/remote-sales/agent/partnerships/$PARTNERSHIP_GUID/stock?updated_since=$SYNC_TOKEN&limit=200" \
  -H "Authorization: Bearer $TOKEN"

# живая позиция — все поля; надгробие — только идентификатор, updated и причина:
{ "vehicle_guid": "…", "removed": true, "removed_reason": "unavailable", "updated": "2026-10-13T09:30:00Z" }
  • Повторы возможны. Порядок инкремента задаёт журнал изменений стока партнёрства; изменения во время обхода попадут в следующий инкремент, одна позиция может прийти повторно, updated не обязан возрастать. Обрабатывайте позиции идемпотентно: последняя запись по vehicle_guid побеждает.
  • Надгробия. Снятая позиция приходит с removed: true, остальных полей нет. removed_reason: unavailable — продан, удалён или снят с публикации; excluded — организатор исключил автомобиль из стока программы.
  • Фильтры brand/model надгробия не отсекают: инкремент с фильтром может содержать надгробия автомобилей, которых вы не видели — просто игнорируйте неизвестные.
  • Токен старше 30 дней — 410 sync_token_expired: сделайте полный обход заново и сохраните свежий токен.
  • cursor и sync_token непрозрачны и привязаны к партнёрству — не разбирайте и не переносите между партнёрствами.
  • Признаки брони у позиции: reserved, reserved_by_me, reserved_until; кто бронировал — не раскрывается.
  • Вебхуки stock.* ускоряют реакцию, но не заменяют сверку: для надёжности периодически запускайте инкрементальный обход.

Идемпотентность

  • POST /agent/reservations и POST /agent/deals требуют заголовок Idempotency-Key (до 128 символов); без него — 400 idempotency_key_required.
  • Приглашение (POST /owner/partnerships) и заявка (POST /agent/partnerships) принимают заголовок по желанию.
  • Повтор с тем же ключом в течение 24 часов возвращает первый ответ с заголовком Idempotency-Replayed: true и не создаёт второй ресурс; тот же ключ с другим телом — 409 idempotency_key_reused, пока первый запрос выполняется — 409 idempotency_in_progress.
  • Переходы состояний (accept, cancel, confirm и др.) повторять безопасно: недопустимый переход — 409 invalid_state.

Вебхуки

Адрес и секрет подписи задаются в карточке ключа, заголовки, подпись, повторы и таймаут — в общем разделе «Вебхуки и подпись» (ответ 2xx за 10 секунд, повторы через 1, 5, 15, 60 и 300 секунд). Ключ получает события тех ролей, чьи скоупы у него есть; чужие события не приходят. Возможны повторные доставки — обработчик должен быть идемпотентным по id.

POST https://crm.example.ru/car2b/webhook
Content-Type: application/json
X-Car2B-Event: reservation.created
X-Car2B-Delivery: 7f0d3c1a-5e2b-4a7c-9d61-0b8e4f2a1c33
X-Car2B-Signature: sha256=3f5a9c…e1

{ "id": "7f0d3c1a-…", "type": "reservation.created", "occurred_at": "2026-10-13T09:30:00Z", "data": { … } }

События организатора (ключ с назначением «организатор»):

СобытиеКогда
partnership.requestedагент подал заявку на вашу программу
partnership.acceptedпартнёрство активно (вы приняли заявку или агент принял ваше приглашение)
partnership.declinedваше приглашение отклонено
partnership.terminatedпартнёрство завершено
reservation.createdсоздана бронь вашего автомобиля
reservation.cancelledбронь снята
reservation.expiring_soonбронь истекает через 15 минут (однократно)
reservation.expiredбронь истекла
deal.createdсделку создала вторая сторона (агент зафиксировал покупку)
deal.confirmedсделку подтвердила одна сторона — событие получает вторая
deal.completedсделка завершена
deal.cancelledсделка отменена
deal.disputedзаявлено расхождение по сделке

События агента (ключ с назначением «агент»):

СобытиеКогда
partnership.invitedорганизатор пригласил вашу организацию
partnership.acceptedпартнёрство активно (организатор принял вашу заявку или вы приняли приглашение)
partnership.declinedваша заявка отклонена; при удалении программы организатором — с признаком program_deleted: true
partnership.terminatedпартнёрство завершено
reservation.createdбронь создана не через ваш ключ (например, из интерфейса)
reservation.cancelledбронь снята
reservation.expiring_soonбронь истекает через 15 минут (однократно)
reservation.expiredбронь истекла
deal.createdсделка создана не вами (получает сторона, не инициатор)
deal.confirmedсделку подтвердила одна сторона — событие получает вторая
deal.completedсделка завершена
deal.cancelledсделка отменена
deal.disputedзаявлено расхождение по сделке
level.changedдостигнут новый уровень программы
stock.vehicle_addedавтомобиль появился в доступном стоке
stock.price_changedцена автомобиля изменилась
stock.vehicle_soldавтомобиль продан
stock.vehicle_unavailableавтомобиль снят, в архиве или удалён

Точный состав data каждого события — в спецификации (раздел webhooks). Журнал доставок: GET /webhooks/deliveries — доступен ключу с любым из read-скоупов.

Лимиты

ЧтоЗначение
Запросов в секунду на ключ10, всплеск до 50
Тело запроса64 КБ
Страница списка (limit)от 1 до 200, по умолчанию 50
Idempotency-Keyдо 128 символов, помнится 24 часа
Срок бронидо 2 часов, бесплатно
Хранение надгробий стока30 дней
Активных ключей на организацию для каждого назначения5

При превышении лимита запросов приходит 429 с заголовком Retry-After — подождите указанное число секунд. Списки используют курсорную пагинацию: limit и cursor из next_cursor.

Ошибки

Ошибки приходят в формате application/problem+json. В errors[] — поле, машинный код и сообщение на русском.

HTTPcodeКогда
400 malformed, invalid_value, idempotency_key_required невалидный JSON, неизвестное поле, неверный тип; неверный параметр запроса; не передан Idempotency-Key там, где он обязателен
401 token_invalid, token_expired, client_revoked обновите токен через /oauth/token и повторите запрос один раз; client_revoked — ключ отозван или неизвестен
403 scope_missing, permission_missing, organization_unverified, organization_blocked, subscription_required, ip_not_allowed нет нужного скоупа; у владельца ключа нет доступа к разделу «Удалённый ОП»; организация не верифицирована или заблокирована; нет активной подписки при включённой платной модели; запрос с неразрешённого IP
404 not_found ресурса нет либо ваша организация не его участник — чужие ресурсы неотличимы от несуществующих; агентские операции работают только внутри active-партнёрства
409 invalid_state, already_exists, program_blocked, geography_restricted, self_partnership, partnership_not_active недопустимый переход состояния; партнёрство уже есть; программа заблокирована площадкой; ограничение по географии; партнёрство с самим собой; партнёрство не active
409 reservation_disabled, reservations_blocked, reservation_active_limit, reservation_daily_limit, vehicle_unavailable брони выключены или заблокированы; достигнут лимит активных или суточных броней; автомобиль недоступен — правила те же, что в кабинете
409 idempotency_in_progress, idempotency_key_reused первый запрос с этим ключом ещё выполняется; ключ уже использован с другим телом
410 sync_token_expired sync_token старше 30 дней — нужен полный обход стока
422 invalid_enum, invalid_value, required_field_missing, unknown_vehicle, unknown_organization поля разобраны, но не проходят правила; unknown_organization — приглашена неизвестная организация
429 rate_limited, daily_quota_exceeded превышен лимит, в ответе заголовок Retry-After
503 feature_disabled, temporarily_unavailable API выключен или проверка ключа временно недоступна; в ответе Retry-After, повторите позже

Спецификация

Точное описание ручек, тел запросов и ответов, а также вебхуков — в OpenAPI-спецификации: интерактивная версия и файл YAML для генерации клиента.

Общие вопросы про ключи — документация API; по интеграции — контакты Car2B.