Car Image API

Clients · Outils concessionnaires, import et VIN

Comment transformer un VIN ou une liste de stock en photo pour chaque véhicule dans un logiciel de concession

Comment les outils pour concessionnaires et importateurs passent d'un VIN ou d'une ligne de fiche de stock à une image studio sur chaque fiche : décodage VIN gratuit, resolve, identifiants stables et URL signées.

Le problème : la fiche a un VIN des mois avant d'avoir une photo

Une fiche de stock dans un CRM, un ERP ou un système de gestion de concession (DMS) naît sous la forme d’un VIN, d’un numéro de stock et d’un prix. La photo arrive quand le photographe passe sur le parc, et pour certains véhicules, elle n’arrive jamais : un véhicule en transit, une reprise encore en reconditionnement, une importation dont le nom de modèle ne correspond à rien dans le flux d’images du DMS, une voiture des années 1990 dont personne n’a acquis de photo presse sous licence. Chaque écran qui affiche cette fiche — la liste du vendeur, l’estimation de l’acheteur, le devis du client — affiche à la place un espace réservé gris.

Les outils pour concessionnaires et importateurs qui utilisent l'API remplissent cet espace réservé à partir de ce que la fiche contient déjà. Si elle a un VIN, l'image n'est qu'à un décodage gratuit. Si elle n'a qu'une ligne dans une liste de stock, la résolution en texte libre fait l'affaire. Dans les deux cas, la fiche finit par contenir un identifiant de véhicule stable, et chaque écran affiche la même voiture de la même façon.

À quoi ressemble une intégration pour concessionnaire

  • Décoder dès la saisie. Le VIN est décodé une seule fois, à la création de la fiche, et l'identifiant du véhicule dans le catalogue est stocké à côté. Rien en aval n'écrit de marque ni de modèle.
  • Une vue pour la liste, une pour la fiche. Une vue side de profil dans un cadre large pour les listes de stock ; front-3-4 pour la fiche détaillée et le devis.
  • Un large historique d'années. Le stock importé et d'occasion remonte aux années 1990, et son rendu est le même que celui des nouveaux arrivages : le catalogue couvre de 1990 à l'année-modèle en cours, modèles du marché intérieur japonais inclus.
  • Des URL signées dans l'application. Le CRM est un navigateur : il charge donc des URL signées sans clé, émises par le serveur, jusqu'à cinquante par appel, et renouvelées pour les fiches qui restent actives plus d'une semaine.

Étape 1 : décoder le VIN, gratuitement

GET /api/v1/vin/{vin} est gratuit et ne nécessite qu'une clé API. Il renvoie l'année, la marque, le modèle, la finition, le type de carrosserie et le moteur décodés, ainsi que tous les autres attributs connus des données vPIC de la NHTSA et, lorsque le catalogue contient le véhicule, un objet vehicle avec son identifiant stable et un image_path prêt à l'emploi. Les VIN partiels fonctionnent aussi : au moins cinq caractères, * pour chaque position inconnue, plus une indication year facultative.

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

Étape 1b : quand vous n'avez qu'une ligne sur une fiche de stock

Les véhicules d’importation arrivent souvent sous forme de tableur : une année, une marque, un nom de modèle dans l’orthographe choisie par la maison de ventes, un code de grade ou de finition. POST /api/v1/images/resolve est gratuit et déterministe : il extrait de la phrase les mots désignant l’année, la couleur et la vue, interroge le catalogue avec le reste, et renvoie des paramètres exacts accompagnés d’un indice de confiance exploitable.

Résoudre une ligne du fichier de stock
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 (extrait)
{
  "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…"
}

Le véhicule est le bon (le Toyota Voxy 2019), mais la confiance est low, car « ZS » est une finition que le catalogue ne connaît pas et la correspondance a dû la contourner. Lors d’un import par lots, considérez high comme acquis, et soumettez medium et low à une personne avec la liste candidates. Lorsque la feuille contient déjà l’année et la marque dans des colonnes distinctes, envoyez-les dans les champs year et make et ne laissez que le modèle dans query ; la correspondance est bien plus nette. Une phrase sans aucun terme lié à un véhicule renvoie une 400, et une marque sans modèle correspondant renvoie une 404 qui liste les années de la marque afin que vous puissiez demander le modèle.

Étape 2 : conserver l’identifiant du véhicule dans la fiche

Quelle que soit la voie empruntée, stockez vehicle.id (ou params.vehicle_id) dans la fiche de stock, dans une simple colonne de type chaîne. Il est déterministe et permanent : le même véhicule a le même identifiant à chaque déploiement et après chaque version du catalogue, il peut donc servir de clé étrangère en toute sécurité. Dès lors, chaque requête d’image est vehicle=veh_…, impossible à mal orthographier, et une marque renommée par un constructeur ou reclassée par une source ne casse jamais une URL stockée.

Étape 3 : une vue pour la liste, une pour la page de détail

Les listes de stock sont larges et peu hautes, la vue de profil dans un cadre 2:1 est donc le choix habituel : chaque véhicule est orienté dans le même sens et repose sur la même ligne de base. trim=1&padding=6 recadre sur la voiture avant l’ajustement, si bien qu’un Hilux et une Yaris remplissent la ligne de la même manière ; fit=contain maintient le canevas à exactement 640×320 avec des marges transparentes. La page de détail utilise front-3-4 en 1024, et les deux partagent un cache : une peinture non spécifiée est argent sur les deux.

L’image de liste d’une fiche
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

Étape 4 : l’afficher dans le CRM via des URL signées

Le CRM s'exécute dans un navigateur, et un navigateur ne doit jamais détenir la clé API. Lorsqu'un écran affiche une page du stock, le serveur émet une URL signée par fiche avec POST /api/v1/image-urls (jusqu'à cinquante par appel, un crédit chacune) et la page les charge comme n'importe quelle image. Deux paramètres comptent pour une fiche qui reste en stock pendant des mois :

  • ttl_seconds: 604800 avec renew: true : l'URL continue de fonctionner au-delà du TTL de sept jours, et chaque semaine supplémentaire au cours de laquelle quelqu'un ouvre réellement la fiche coûte un crédit de plus. Les semaines où personne ne consulte la voiture ne coûtent rien.
  • Une Idempotency-Key par page de synchronisation : une requête relancée rejoue la première réponse et rien n'est émis deux fois.

Restreignez la clé émettrice à l'origine du CRM dans le tableau de bord : une URL qui fuite vers un autre site renverra 403. Révoquer la clé invalide toutes les URL qu'elle a émises. Si le solde vient à s'épuiser, un renouvellement renvoie 402 et la même URL refonctionne dès que les crédits arrivent ; le rechargement automatique élimine ce cas d'échec. Le cycle de vie complet est décrit dans la documentation des URL signées.

Réception des véhicules et liste de stock, avec le 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 et 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, … }) et client.createImageUrls(…), comme ci-dessus.
  • MCP : decode_vin, resolve_vehicle, get_car_image et create_car_image_urls font partie de l’ensemble d’outils de base, de sorte qu’un assistant ayant le système de stock en contexte peut répondre à « photo du stock 4471 » à partir du VIN de la fiche. La configuration est décrite sur la page MCP ; les champs et erreurs du décodeur figurent dans la documentation VIN.

Questions fréquentes

Combien coûte une photo sur chaque fiche de stock ?
Le décodage VIN est gratuit. La première image d’un véhicule, d’une vue et d’une peinture coûte un crédit, puis elle est mise en cache pour tout le monde ; la revérifier avec If-None-Match est gratuit. Une URL signée coûte un crédit à sa création et, avec renew: true, un crédit de plus pour chaque semaine supplémentaire au cours de laquelle elle est effectivement ouverte. Un groupe de concessions avec quelques milliers de véhicules en stock dépense quelques milliers de crédits une seule fois, puis presque rien.
Le VIN est décodé mais vehicle est null. Que faire ?
Le décodeur a reconnu la marque, le modèle et l’année, mais le catalogue n’a aucune entrée pour cette combinaison : une remorque, certains châssis utilitaires, une appellation que les sources n’ont pas encore répertoriée. Affichez les champs décodés, cherchez dans le catalogue avec GET /api/v1/vehicles?q= au cas où l’orthographe diffère, et demandez le véhicule sur le tableau des demandes ; POST /api/v1/requests vote pour une demande ouverte au lieu d’en créer un doublon.
Le décodage de VIN couvre-t-il les véhicules importés et ceux jamais vendus aux États-Unis ?
Le décodeur s’appuie sur les données vPIC de la NHTSA : le VIN d’un constructeur déclaré auprès de la NHTSA se décode donc entièrement. Un véhicule jamais homologué pour les États-Unis se décode souvent en un constructeur et une année sans modèle, et certains ne se décodent pas du tout. Pour ces véhicules, effectuez plutôt la résolution à partir du certificat d’immatriculation : le catalogue lui-même est mondial, à partir de 1990, et les modèles du marché intérieur japonais comme le Toyota Voxy y figurent.
Le rendu montre-t-il la finition indiquée dans la fiche ?
Non. Le catalogue s’indexe sur la marque, le modèle et l’année : un coupé EX-V6 et une berline LX donnent donc le même rendu d’Accord 2003. Conservez la finition et le body_class issus du décodage sous forme de texte à côté de l’image, et ne présentez jamais le rendu comme le véhicule précis.
Le site web du concessionnaire peut-il utiliser les mêmes URL que le CRM ?
Émettez-les à partir d'une clé dont les origines autorisées incluent le site : une URL qu'un navigateur charge depuis un autre site renvoie 403 origin_not_allowed. Les chargements sans Referer, comme ceux d'un client de messagerie, sont acceptés. Le CRM et le site web peuvent partager une clé et un même ensemble d'URL, ou utiliser chacun sa propre clé afin de pouvoir révoquer l'une sans l'autre.