Car Image API

Klienci · Narzędzia dla dealerów, importu i VIN

Jak zamienić VIN lub arkusz stanów magazynowych w zdjęcie każdego pojazdu w oprogramowaniu dla dealerów

Jak narzędzia dla dealerów i importerów przechodzą od numeru VIN lub wiersza arkusza stanów magazynowych do studyjnego obrazu przy każdym rekordzie: bezpłatne dekodowanie VIN, resolve, stabilne identyfikatory i podpisane adresy URL.

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 side w szerokim kontenerze dla list pojazdów na stanie; front-3-4 dla 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.

Dekodowanie
curl -s -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/vin/1HGCM82633A004352"
200 OK (skrócone)
{
  "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…"
}

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ę.

Rozpoznanie pozycji z arkusza stanów magazynowych
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"}'
200 OK (skrócone)
{
  "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.

Obraz na liście dla jednego rekordu
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

Krok 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: 604800 z renew: 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-Key na 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.

Przyjęcie pojazdu i lista stanu z TypeScript SDK
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

npx @meterapp/car-image
# 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, … }) i client.createImageUrls(…), jak wyżej.
  • MCP: decode_vin, resolve_vehicle, get_car_image i create_car_image_urls należą 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.