Проблема: VIN в записи появляется за несколько месяцев до фотографии
Запись об автомобиле в наличии в CRM, ERP или системе управления дилерским центром (DMS) начинается с VIN, складского номера и цены. Фото появляется, когда приходит фотограф площадки, а для некоторых машин не появляется вовсе: автомобиль в пути, машина, сданная в трейд-ин и ещё проходящая предпродажную подготовку, импортный автомобиль, название модели которого не совпадает ни с чем в фиде изображений DMS, машина 1990-х, на пресс-фото которой никто не покупал лицензию. На каждом экране с этой записью — в списке продавца, в оценке у закупщика, в предложении клиенту — вместо фото серая заглушка.
Инструменты для дилеров и импорта, работающие на API, заполняют эту заглушку тем, что уже есть в записи. Если в ней есть VIN, до изображения — одно бесплатное декодирование. Если есть только строка в складской ведомости, поможет resolve по свободному тексту. В любом случае в записи оказывается постоянный id автомобиля, и на каждом экране один и тот же автомобиль выглядит одинаково.
Как выглядит интеграция для дилера
- Расшифровка при поступлении. VIN расшифровывается один раз, при создании записи, и id автомобиля из каталога сохраняется рядом с ним. Дальше по цепочке марка и модель нигде не пишутся текстом.
- Один ракурс для списка, другой для карточки. Прямой
sideв широкой рамке для списков автомобилей в наличии;front-3-4для страницы автомобиля и коммерческого предложения. - Глубокий охват по годам. Импортные и подержанные автомобили уходят в 1990-е, и они рендерятся так же, как новые поступления: каталог охватывает период с 1990 по текущий модельный год, включая модели для внутреннего японского рынка.
- Подписанные URL внутри приложения. CRM работает в браузере, поэтому загружает подписанные URL без ключа, выпущенные сервером, — до пятидесяти за вызов — и обновляет их для записей, которые живут дольше недели.
Шаг 1: бесплатно расшифруйте VIN
GET /api/v1/vin/{vin} бесплатен, и для него нужен только API-ключ. Он возвращает расшифрованные год, марку, модель, комплектацию, тип кузова, двигатель и все остальные атрибуты, известные данным vPIC от NHTSA, а если автомобиль есть в каталоге, то и объект vehicle с его постоянным id и готовым image_path. Частичные VIN тоже подходят: не менее пяти символов, * на каждой неизвестной позиции и необязательная подсказка year.
curl -s -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
"https://carimage.dev/api/v1/vin/1HGCM82633A004352"{
"data": {
"vin": "1HGCM82633A004352",
"valid": true,
"year": 2003,
"make": "Honda",
"model": "Accord",
"trim": "EX-V6",
"body_class": "Coupe",
"vehicle": {
"id": "veh_3qfyk22gfhsx3",
"make": "Honda",
"model": "Accord",
"year": 2003,
"image_path": "/api/v1/images/car?vehicle=veh_3qfyk22gfhsx3"
}
},
"request_id": "req_01j9x…"
}trim и body_class в записи как текст и показывайте их рядом с изображением, не утверждая, что изображение их отражает. VIN с ошибкой в контрольной цифре всё равно расшифровывается — с valid: false и указанием ошибки, так что опечатка в складской ведомости не заводит в тупик.Шаг 1b: когда у вас есть только строка в складской ведомости
Импортные автомобили часто поступают в виде таблицы: год, марка, название модели в том написании, которое использовал аукцион, код комплектации или грейда. POST /api/v1/images/resolve бесплатен и детерминирован: он извлекает из фразы слова, обозначающие год, цвет и ракурс, ищет по каталогу по оставшейся части и возвращает точные параметры с уровнем уверенности, на который можно опираться.
curl -s -X POST https://carimage.dev/api/v1/images/resolve \
-H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "2019 toyota voxy zs side"}'{
"data": {
"params": {
"vehicle_id": "veh_9w0nvw7jphp6r",
"make": "toyota",
"model": "voxy",
"year": 2019,
"view": "side",
"color": "silver"
},
"display": {
"make_name": "TOYOTA",
"model_name": "VOXY"
},
"confidence": "low",
"image_path": "/api/v1/images/car?make=toyota&model=voxy&year=2019&view=side&color=silver"
},
"request_id": "req_01j9x…"
}Автомобиль определён верно (Toyota Voxy 2019 года), но уверенность — low, потому что «ZS» — это комплектация, которую каталог не знает, и её пришлось обойти при сопоставлении. При пакетном импорте считайте high готовым результатом, а medium и low передавайте человеку вместе со списком candidates. Если в таблице год и марка уже указаны в отдельных столбцах, передавайте их в полях year и make, а в query оставляйте только модель — сопоставление станет намного точнее. Фраза без слов, относящихся к автомобилю, возвращает 400, а марка без подходящей модели — 404 со списком годов этой марки, чтобы вы могли уточнить модель.
Шаг 2: сохраните id автомобиля в записи
Каким бы путём вы к нему ни пришли, сохраните vehicle.id (или params.vehicle_id) в записи складского автомобиля как обычный строковый столбец. Он детерминирован и постоянен: у одного и того же автомобиля один и тот же id при каждом развёртывании и после каждого релиза каталога, поэтому его можно безопасно использовать как внешний ключ. С этого момента каждый запрос изображения — это vehicle=veh_…, где невозможно допустить опечатку, а марка, переименованная производителем или иначе заведённая источником, никогда не сломает сохранённый URL.
Шаг 3: один ракурс для списка, другой для страницы с подробностями
Списки автомобилей в наличии широкие и невысокие, поэтому обычно выбирают прямой вид сбоку в рамке 2:1: каждый автомобиль смотрит в одну сторону и стоит на одной базовой линии. trim=1&padding=6 обрезает изображение по автомобилю перед вписыванием, поэтому Hilux и Yaris заполняют строку одинаково; fit=contain сохраняет холст ровно 640×320 с прозрачными полями. На странице с подробностями используется front-3-4 размером 1024, и у обоих общий кеш: если цвет не указан, он серебристый в обоих случаях.
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
"https://carimage.dev/api/v1/images/car?vehicle=veh_3qfyk22gfhsx3&view=side&w=640&h=320&fit=contain&trim=1&padding=6&format=webp" \
-o stock-4471.webpШаг 4: показывайте изображения в CRM через подписанные URL
CRM работает в браузере, а в браузере никогда не должен храниться API-ключ. Когда на экране отображается страница складских позиций, сервер создаёт по одному подписанному URL на запись с помощью POST /api/v1/image-urls (до пятидесяти за вызов; изображение каждого URL засчитывается как изображение автомобиля за месяц), и страница загружает их как обычные изображения. Для записи, которая месяцами остаётся в наличии, важны две настройки:
ttl_seconds: 604800сrenew: true: URL продолжает работать после семидневного TTL, а продление засчитывает изображение один раз в месяце, на который оно приходится, и ничего сверх, если оно уже было учтено в этом месяце. Месяцы, когда автомобиль никто не смотрит, не учитываются.- Отдельный
Idempotency-Keyдля каждой страницы синхронизации: повторный запрос воспроизводит первый ответ и ничего не выпускает дважды.
Ограничьте ключ, выпускающий URL, источником (origin) CRM в панели управления — и URL, попавший на другой сайт, вернёт 403. Отзыв ключа аннулирует все выпущенные им URL. Если изображения автомобилей по тарифу на месяц израсходованы, продление вернёт 402, а тот же URL снова заработает после сброса лимита или повышения тарифа. Полный жизненный цикл описан в документации по подписанным URL.
import { CarImageClient } from "@meterapp/car-image-sdk";
const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });
// On intake: VIN in, vehicle id on the record. Free.
const { data: decoded } = await client.decodeVin(stock.vin);
if (decoded.vehicle) {
stock.vehicleId = decoded.vehicle.id; // veh_…, permanent; the record never spells a car again
stock.trim = decoded.trim; // shown next to the image, never claimed by it
}
// When the CRM draws the stock list: one signed URL per record, up to fifty per call.
// The browser loads them; the key never leaves the server.
const { data: urls } = await client.createImageUrls(
page.map((row) => ({
vehicle: row.vehicleId, view: "side",
width: 640, height: 320, fit: "contain", trim: true, padding: 6, format: "auto",
})),
{ ttlSeconds: 604_800, renew: true, renewDays: 365, idempotencyKey: `stock-list:${syncRunId}:${pageIndex}` }
);CLI, SDK и MCP
# decode for free; the output names the vehicle id and a ready-to-run image command
npx @meterapp/car-image vin 1HGCM82633A004352
npx @meterapp/car-image vin 1HGCM82633A004352 --json | jq -r .data.vehicle.id
# a partial VIN from a faded door sticker: at least five characters, * for each unknown position
npx @meterapp/car-image vin "1HGCM826*3A" --year 2003
# the list image for one record
npx @meterapp/car-image get --vehicle veh_3qfyk22gfhsx3 --view side --width 640 --height 320 --fit contain --trim --padding 6 --format webp --out stock-4471.webp- SDK:
client.decodeVin(vin, { year? }),client.resolve(query),client.getImage({ vehicle, … })иclient.createImageUrls(…), как показано выше. - MCP:
decode_vin,resolve_vehicle,get_car_imageиcreate_car_image_urlsвходят в базовый набор инструментов, поэтому ассистент, у которого в контексте есть складская система, может ответить на запрос «фото для позиции 4471» по VIN из записи. Настройка описана на странице MCP; поля и ошибки декодера — в документации по VIN.
Часто задаваемые вопросы
- Сколько стоит фото для каждого автомобиля в наличии?
- Декодирование VIN бесплатно. Каждый автомобиль в каждом цвете окраски и ракурсе — это одно изображение автомобиля, которое учитывается один раз в месяц, как бы часто и в каком бы размере его ни показывали; повторная проверка через If-None-Match бесплатна. Подписанный URL засчитывает своё изображение при создании и, с renew: true, ещё раз в каждом последующем месяце, в котором его открывают. Дилерская группа с несколькими тысячами автомобилей на складе показывает несколько тысяч изображений автомобилей в месяц, поэтому подбирайте тариф под количество выставленных автомобилей.
- VIN декодируется, но vehicle равен null. Что делать?
- Декодер определил марку, модель и год, но в каталоге нет записи для этого сочетания: прицеп, некоторые коммерческие шасси, модель, которую источники ещё не внесли. Покажите декодированные поля, поищите в каталоге через GET /api/v1/vehicles?q= на случай, если написание отличается, и запросите автомобиль на доске запросов; POST /api/v1/requests добавляет голос к открытому запросу, а не создаёт дубликат.
- Охватывает ли декодирование VIN импортные автомобили и машины, никогда не продававшиеся в США?
- Декодер использует данные vPIC от NHTSA, поэтому VIN производителя, который подаёт данные в NHTSA, расшифровывается полностью. Автомобиль, никогда не сертифицированный для США, часто расшифровывается лишь до производителя и года без модели, а некоторые не расшифровываются вовсе. Для таких автомобилей определяйте модель по регистрационному документу: сам каталог охватывает весь мир начиная с 1990 года, и в нём есть модели японского внутреннего рынка, например Toyota Voxy.
- Показывает ли рендер комплектацию, указанную в записи?
- Нет. Каталог ориентируется на марку, модель и год, поэтому купе EX-V6 и седан LX отображаются как один и тот же Accord 2003 года. Храните trim и body_class из расшифровки VIN как текст рядом с изображением и никогда не выдавайте рендер за изображение конкретного экземпляра.
- Может ли сайт дилера использовать те же URL, что и CRM?
- Создавайте их ключом, в списке разрешённых источников (allowed origins) которого указан сайт, — тогда URL, загруженный браузером с любого другого сайта, вернёт 403 origin_not_allowed. Загрузки без Referer, например из почтового клиента, проходят. CRM и сайт могут использовать один ключ и один набор URL или отдельный ключ каждый, чтобы любой из них можно было отозвать независимо.