Car Image API

Clienti · Strumenti per concessionari, importazione e VIN

Come trasformare un VIN o un elenco dello stock in una foto per ogni veicolo in un software per concessionari

Come gli strumenti per concessionari e importatori passano da un VIN o da una riga del foglio di stock a un'immagine da studio su ogni record: decodifica VIN gratuita, resolve, ID stabili e URL firmati.

Il problema: il record ha un VIN mesi prima di avere una foto

Una scheda di veicolo in giacenza in un CRM, in un ERP o in un gestionale per concessionari (DMS) nasce come VIN, numero di stock e prezzo. La foto arriva quando arriva il fotografo del piazzale, e per alcuni veicoli non arriva mai: un'unità in transito, una permuta ancora in fase di ricondizionamento, un veicolo d'importazione il cui nome di modello non corrisponde a nulla nel feed di immagini del DMS, un'auto degli anni '90 di cui nessuno ha mai acquistato in licenza una foto stampa. Ogni schermata che mostra quella scheda, l'elenco del venditore, la valutazione dell'acquirente, il preventivo del cliente, mostra invece un segnaposto grigio.

Gli strumenti per concessionari e importatori sull’API riempiono quel segnaposto con ciò che il record contiene già. Se ha un VIN, l’immagine è a una decodifica gratuita di distanza. Se ha solo una riga in un elenco di stock, ci arriva il resolve a testo libero. In ogni caso il record finisce per contenere un id veicolo stabile e ogni schermata mostra la stessa auto nello stesso modo.

Com’è fatta un’integrazione per concessionari

  • Decodifica all'acquisizione. Il VIN viene decodificato una sola volta, alla creazione del record, e l'ID del veicolo nel catalogo viene salvato accanto a esso. Nessun passaggio a valle scrive una marca o un modello.
  • Una vista per l'elenco, una per il dettaglio. Una vista side pura in un riquadro largo per gli elenchi dello stock; front-3-4 per la pagina di dettaglio e il preventivo.
  • Anni lontani. Lo stock di importazione e usato risale agli anni ’90 e viene renderizzato come i nuovi arrivi: il catalogo copre dal 1990 all’anno modello corrente, incluse le denominazioni del mercato interno giapponese.
  • URL firmati all'interno dell'app. Il CRM gira nel browser, quindi carica URL firmati senza chiave generati dal server, fino a cinquanta per chiamata, rinnovandoli per i record che restano attivi più di una settimana.

Passo 1: decodificare il VIN, gratuitamente

GET /api/v1/vin/{vin} è gratuito e richiede solo una chiave API. Restituisce anno, marca, modello, allestimento, tipo di carrozzeria e motore decodificati, insieme a ogni altro attributo noto ai dati vPIC della NHTSA e, quando il catalogo contiene il veicolo, un oggetto vehicle con il suo id stabile e un image_path già pronto. Funzionano anche i VIN parziali: almeno cinque caratteri, * per ogni posizione sconosciuta, più un suggerimento year facoltativo.

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

Passaggio 1b: quando si dispone solo di una riga di un elenco di inventario

Le auto d'importazione arrivano spesso sotto forma di foglio di calcolo: un anno, una marca, un nome di modello scritto come l'ha scritto la casa d'aste, un codice di grado o di allestimento. POST /api/v1/images/resolve è gratuito e deterministico: estrae dalla frase le parole relative ad anno, colore e vista, cerca nel catalogo con ciò che resta e restituisce parametri esatti con un livello di confidenza su cui può basarsi.

Risoluzione di una riga del listino di magazzino
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 (estratto)
{
  "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…"
}

Il veicolo è corretto (la Toyota Voxy del 2019) ma la confidenza è low, perché "ZS" è un allestimento che il catalogo non conosce e la corrispondenza ha dovuto aggirarlo. In un’importazione batch, consideri high come risolto e sottoponga medium e low a una persona insieme all’elenco candidates. Quando il foglio ha già anno e marca in colonne separate, li invii nei campi year e make e lasci solo il modello in query; la corrispondenza diventa molto più precisa. Una frase senza termini relativi a veicoli restituisce un 400, e una marca senza un modello corrispondente restituisce un 404 che elenca gli anni della marca, così può chiedere il modello.

Passo 2: conservare l'id del veicolo nel record

Qualunque sia il percorso seguito, memorizzi vehicle.id (o params.vehicle_id) nel record di magazzino come semplice colonna di tipo stringa. È deterministico e permanente: lo stesso veicolo ha lo stesso id in ogni deploy e dopo ogni rilascio del catalogo, quindi è sicuro come chiave esterna. Da quel momento ogni richiesta di immagine è vehicle=veh_…, che non può essere scritta in modo errato, e una marca rinominata da un produttore o riclassificata da una fonte non rompe mai un URL memorizzato.

Passaggio 3: una vista per l'elenco, una per la pagina di dettaglio

Gli elenchi di inventario sono larghi e bassi, quindi la vista laterale dritta in un riquadro 2:1 è la scelta abituale: ogni veicolo è rivolto nella stessa direzione e poggia sulla stessa linea di base. trim=1&padding=6 ritaglia sull’auto prima di adattarla, così un Hilux e una Yaris riempiono la riga allo stesso modo; fit=contain mantiene la tela esattamente a 640×320 con margini trasparenti. La pagina di dettaglio usa front-3-4 a 1024 e le due condividono una cache: una vernice non specificata è argento in entrambe.

L'immagine di elenco per un record
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

Passo 4: mostrarle nel CRM tramite URL firmati

Il CRM gira in un browser, e un browser non deve mai contenere la chiave API. Quando una schermata elenca una pagina di veicoli in stock, il server genera un URL firmato per record con POST /api/v1/image-urls (fino a cinquanta per chiamata; l'immagine di ogni URL conta come immagine di veicolo del mese) e la pagina li carica come qualsiasi immagine. Per un record che resta in stock per mesi contano due impostazioni:

  • ttl_seconds: 604800 con renew: true: l'URL continua a funzionare oltre il TTL di sette giorni, e un rinnovo conta l'immagine una volta nel mese in cui avviene, e nulla di più se era già stata conteggiata in quel mese. I mesi in cui nessuno guarda l'auto non contano nulla.
  • Una Idempotency-Key per ogni pagina della sincronizzazione: una richiesta ripetuta riproduce la prima risposta e non genera nulla due volte.

Limiti la chiave di generazione all’origine del CRM dalla dashboard e un URL finito su un altro sito risponderà 403. Revocare la chiave invalida tutti gli URL che ha generato. Se le immagini di veicoli del piano per il mese sono esaurite, un rinnovo risponde 402 e lo stesso URL torna a funzionare quando la quota si azzera o il piano viene aggiornato. Il ciclo di vita completo è nella documentazione sugli URL firmati.

Presa in carico ed elenco dello stock, con l’SDK TypeScript
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 e 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, … }) e client.createImageUrls(…), come sopra.
  • MCP: decode_vin, resolve_vehicle, get_car_image e create_car_image_urls fanno parte del set di strumenti principale, quindi un assistente con il gestionale dello stock nel contesto può rispondere a "foto per lo stock 4471" partendo dal VIN nel record. La configurazione è nella pagina MCP; i campi e gli errori del decoder sono nella documentazione VIN.

Domande frequenti

Quanto costa avere una foto per ogni veicolo in stock?
La decodifica del VIN è gratuita. Ogni veicolo in ciascuna vernice e vista è un'immagine di veicolo, conteggiata una volta al mese indipendentemente da quante volte viene mostrata e a qualsiasi dimensione; ricontrollarla con If-None-Match è gratuito. Un URL firmato conteggia la sua immagine quando viene generato e, con renew: true, un'altra volta in ogni mese successivo in cui viene aperto. Un gruppo di concessionari con qualche migliaio di veicoli in stock mostra qualche migliaio di immagini di veicoli al mese, quindi dimensioni il piano in base allo stock esposto.
Il VIN viene decodificato ma vehicle è null. E ora?
Il decoder conosceva marca, modello e anno, ma il catalogo non ha una voce per quella combinazione: un rimorchio, alcuni telai commerciali, una denominazione che le fonti non hanno ancora registrato. Mostri i campi decodificati, cerchi nel catalogo con GET /api/v1/vehicles?q= nel caso l'ortografia sia diversa e richieda il veicolo nella bacheca delle richieste; POST /api/v1/requests aggiunge un voto a una richiesta aperta invece di duplicarla.
La decodifica VIN copre anche i veicoli importati e quelli mai venduti negli Stati Uniti?
Il decoder si basa sui dati vPIC dell'NHTSA, quindi un VIN di un produttore registrato presso l'NHTSA viene decodificato completamente. Un veicolo mai omologato per gli Stati Uniti spesso viene decodificato solo con produttore e anno, senza modello, e alcuni non vengono decodificati affatto. Per questi veicoli, effettui la risoluzione a partire dal libretto di circolazione: il catalogo è mondiale, dal 1990, e include modelli del mercato interno giapponese come la Toyota Voxy.
Il render mostra l'allestimento indicato nel record?
No. Il catalogo si basa su marca, modello e anno, quindi una coupé EX-V6 e una berlina LX vengono renderizzate come la stessa Accord 2003. Conservi trim e body_class della decodifica come testo accanto all'immagine e non descriva mai il render come l'esemplare specifico.
Il sito web del concessionario può usare gli stessi URL del CRM?
Li generi da una chiave le cui origini consentite includono il sito: un URL caricato da un browser su qualsiasi altro sito risponde 403 origin_not_allowed. I caricamenti senza Referer, come quelli di un client email, passano. Il CRM e il sito web possono condividere una chiave e un unico insieme di URL, oppure usare una chiave ciascuno, così da poterle revocare singolarmente.