Problem: rekord ma VIN na wiele miesięcy przed zdjęciem
Rekord pojazdu w CRM, ERP lub systemie do zarządzania salonem (DMS) zaczyna się od numeru VIN, numeru magazynowego i ceny. Zdjęcie pojawia się, gdy przyjdzie fotograf, a dla części pojazdów nie pojawia się nigdy: auto w transporcie, samochód z odkupu wciąż w przygotowaniu, import, którego nazwa modelu nie pasuje do niczego w źródle zdjęć DMS, auto z lat 90., do którego nikt nie wykupił licencji na zdjęcie prasowe. Każdy ekran z tym rekordem – lista sprzedawcy, wycena dla skupującego, oferta dla klienta – pokazuje zamiast zdjęcia szary obrazek zastępczy.
Narzędzia dla dealerów i importerów korzystające z API wypełniają to miejsce na podstawie danych, które rekord już zawiera. Jeśli ma VIN, od obrazu dzieli go jedno bezpłatne dekodowanie. Jeśli ma tylko wiersz w zestawieniu stanu magazynowego, wystarczy resolve z dowolnego tekstu. Tak czy inaczej rekord otrzymuje stały identyfikator pojazdu, a każdy ekran pokazuje ten sam samochód w ten sam sposób.
Jak wygląda integracja dla dealera
- Dekodowanie przy przyjęciu. VIN jest dekodowany raz, przy tworzeniu rekordu, a identyfikator pojazdu z katalogu zapisywany obok niego. Nic na dalszych etapach nie wpisuje ręcznie marki ani modelu.
- Jeden widok na listę, drugi na stronę szczegółów. Prosty
sidew szerokim kontenerze dla list pojazdów na stanie;front-3-4dla strony szczegółów i oferty. - Starsze roczniki. Pojazdy importowane i używane sięgają lat 90., a renderują się tak samo jak nowości: katalog obejmuje roczniki od 1990 do bieżącego roku modelowego, łącznie z modelami przeznaczonymi na rynek japoński.
- Podpisane adresy URL w aplikacji. CRM działa w przeglądarce, więc wczytuje generowane przez serwer podpisane adresy URL bez klucza, do pięćdziesięciu na wywołanie, odnawiane dla rekordów istniejących dłużej niż tydzień.
Krok 1: bezpłatne dekodowanie numeru VIN
GET /api/v1/vin/{vin} jest bezpłatny i wymaga tylko klucza API. Zwraca zdekodowany rok, markę, model, wersję wyposażenia, typ nadwozia, silnik i każdy inny atrybut znany danym vPIC NHTSA, a gdy pojazd jest w katalogu – obiekt vehicle z jego stałym identyfikatorem i gotową ścieżką image_path. Działają też niepełne numery VIN: co najmniej pięć znaków, * w miejscu każdej nieznanej pozycji oraz opcjonalna wskazówka 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 i body_class jako tekst w rekordzie i wyświetlać je obok obrazu, zamiast twierdzić, że obraz je przedstawia. VIN z błędną cyfrą kontrolną nadal zostanie zdekodowany, z valid: false i wskazanym błędem, więc literówka w arkuszu stanów magazynowych nie oznacza ślepego zaułka.Krok 1b: gdy dostępny jest tylko wiersz z zestawienia stanów magazynowych
Samochody z importu często trafiają w formie arkusza kalkulacyjnego: rok, marka, nazwa modelu w pisowni stosowanej przez dom aukcyjny, kod klasy lub wersji wyposażenia. POST /api/v1/images/resolve jest bezpłatny i deterministyczny: wyodrębnia z frazy słowa oznaczające rok, kolor i widok, przeszukuje katalog pozostałą częścią i zwraca dokładne parametry wraz z poziomem pewności, na podstawie którego można podjąć decyzję.
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…"
}Pojazd jest prawidłowy (Toyota Voxy z 2019 roku), ale poziom pewności to low, ponieważ „ZS” to wersja, której katalog nie zna, więc dopasowanie musiało ją pominąć. Przy imporcie wsadowym należy traktować high jako gotowe, a medium i low przekazać do sprawdzenia człowiekowi wraz z listą candidates. Gdy arkusz ma już rok i markę w osobnych kolumnach, należy wysłać je w polach year i make, a w query zostawić tylko model; dopasowanie będzie wtedy znacznie dokładniejsze. Fraza bez słów opisujących pojazd zwraca 400, a marka bez pasującego modelu zwraca 404 z listą roczników tej marki, aby można było zapytać o model.
Krok 2: zapisanie identyfikatora pojazdu w rekordzie
Niezależnie od wybranej ścieżki należy zapisać vehicle.id (lub params.vehicle_id) w rekordzie pojazdu jako zwykłą kolumnę tekstową. Identyfikator jest deterministyczny i trwały: ten sam pojazd ma ten sam identyfikator przy każdym wdrożeniu i po każdym wydaniu katalogu, więc można go bezpiecznie używać jako klucza obcego. Od tej chwili każde żądanie obrazu to vehicle=veh_…, czego nie da się błędnie wpisać, a marka przemianowana przez producenta lub inaczej sklasyfikowana przez źródło nigdy nie zepsuje zapisanego adresu URL.
Krok 3: jeden widok na listę, drugi na stronę szczegółów
Listy pojazdów na stanie są szerokie i niskie, więc zwykle wybiera się prosty widok z boku w ramce 2:1: każdy egzemplarz jest zwrócony w tę samą stronę i stoi na tej samej linii bazowej. trim=1&padding=6 przycina obraz do samochodu przed dopasowaniem, więc Hilux i Yaris wypełniają wiersz w ten sam sposób; fit=contain utrzymuje płótno dokładnie 640×320 z przezroczystymi marginesami. Strona szczegółów używa front-3-4 w rozmiarze 1024, a oba widoki współdzielą pamięć podręczną: niewskazany kolor lakieru jest w obu przypadkach srebrny.
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.webpKrok 4: wyświetlanie w CRM za pomocą podpisanych adresów URL
CRM działa w przeglądarce, a przeglądarka nigdy nie może przechowywać klucza API. Gdy ekran wyświetla stronę stanów magazynowych, serwer generuje jeden podpisany URL na rekord za pomocą POST /api/v1/image-urls (do pięćdziesięciu na wywołanie; obraz każdego URL-a liczy się jako obraz pojazdu w miesiącu), a strona ładuje je jak każdy inny obraz. W przypadku rekordu, który pozostaje w ofercie przez miesiące, ważne są dwa ustawienia:
ttl_seconds: 604800zrenew: true: URL działa także po upływie siedmiodniowego TTL, a odnowienie liczy obraz raz w miesiącu, w którym nastąpi, i nic więcej, jeśli obraz został już policzony w tym miesiącu. Miesiące, w których nikt nie ogląda samochodu, nie są liczone.- Jeden
Idempotency-Keyna każdą stronę synchronizacji: ponowione żądanie odtwarza pierwszą odpowiedź i niczego nie generuje dwa razy.
Ograniczenie klucza generującego URL-e do originu CRM w panelu sprawia, że URL, który wycieknie na inną stronę, zwraca 403. Odwołanie klucza unieważnia wszystkie wygenerowane nim URL-e. Jeśli obrazy pojazdów z planu na dany miesiąc zostały wykorzystane, odnowienie zwraca 402, a ten sam URL znów działa po odnowieniu limitu lub podniesieniu planu. Pełny cykl życia opisano w dokumentacji podpisanych URL-i.
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 i 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, … })iclient.createImageUrls(…), jak wyżej. - MCP:
decode_vin,resolve_vehicle,get_car_imageicreate_car_image_urlsnależą do podstawowego zestawu narzędzi, więc asystent, który ma w kontekście system magazynowy, może odpowiedzieć na prośbę „zdjęcie dla pozycji 4471” na podstawie VIN z rekordu. Konfiguracja jest opisana na stronie MCP; pola i błędy dekodera znajdują się w dokumentacji VIN.
Najczęściej zadawane pytania
- Ile kosztuje zdjęcie przy każdym pojeździe w ofercie?
- Dekodowanie numeru VIN jest bezpłatne. Każdy pojazd w każdym lakierze i widoku to jeden obraz pojazdu, liczony raz w miesiącu, niezależnie od tego, jak często i w jakim rozmiarze jest wyświetlany; ponowne sprawdzenie go za pomocą If-None-Match jest bezpłatne. Podpisany URL liczy swój obraz w chwili wygenerowania, a z renew: true — jeszcze raz w każdym kolejnym miesiącu, w którym zostanie otwarty. Grupa dealerska z kilkoma tysiącami pojazdów na stanie wyświetla kilka tysięcy obrazów pojazdów miesięcznie, dlatego plan warto dobrać do liczby prezentowanych pojazdów.
- VIN został zdekodowany, ale vehicle ma wartość null. Co dalej?
- Dekoder rozpoznał markę, model i rocznik, ale katalog nie ma wpisu dla tej kombinacji: przyczepa, niektóre podwozia pojazdów użytkowych, oznaczenie modelu, którego źródła jeszcze nie odnotowały. Należy wyświetlić zdekodowane pola, przeszukać katalog za pomocą GET /api/v1/vehicles?q= na wypadek innej pisowni i zgłosić pojazd na tablicy próśb; POST /api/v1/requests oddaje głos na otwartą prośbę zamiast tworzyć duplikat.
- Czy dekodowanie VIN obejmuje pojazdy importowane i te, które nigdy nie były sprzedawane w USA?
- Dekoder korzysta z danych vPIC agencji NHTSA, więc VIN producenta, który zgłasza dane do NHTSA, dekoduje się w pełni. Pojazd nigdy niecertyfikowany na rynek USA często dekoduje się tylko do producenta i roku, bez modelu, a niektóre nie dekodują się wcale. W przypadku takich pojazdów należy zamiast tego skorzystać z dowodu rejestracyjnego: sam katalog jest ogólnoświatowy, obejmuje pojazdy od 1990 r. i zawiera modele z japońskiego rynku wewnętrznego, takie jak Toyota Voxy.
- Czy render pokazuje wersję wyposażenia zapisaną w rekordzie?
- Nie. Katalog opiera się na marce, modelu i roku, więc coupé EX-V6 i sedan LX renderują się jako ten sam Accord z 2003 roku. Wersję wyposażenia i body_class z dekodowania należy zachować jako tekst obok obrazu i nigdy nie opisywać renderu jako konkretnego egzemplarza.
- Czy strona internetowa dealera może używać tych samych adresów URL co CRM?
- Należy generować je kluczem, którego lista dozwolonych originów zawiera tę stronę; URL wczytany przez przeglądarkę z dowolnej innej strony zwraca 403 origin_not_allowed. Wczytania bez nagłówka Referer, np. w kliencie poczty, przechodzą. CRM i strona mogą współdzielić jeden klucz i jeden zestaw URL-i albo używać osobnych kluczy, aby każdy z nich można było unieważnić niezależnie.