Car Image API

Clientes · Ferramentas para revendas, importação e VIN

Como transformar um VIN ou uma planilha de estoque em uma foto para cada veículo no software da concessionária

Como ferramentas de concessionárias e de importação vão de um VIN ou de uma linha da planilha de estoque a uma imagem de estúdio em cada registro: decodificação de VIN gratuita, resolve, ids estáveis e URLs assinadas.

O problema: o registro tem um VIN meses antes de ter uma foto

Um registro de estoque em um CRM, um ERP ou um sistema de gestão de concessionárias (DMS) começa como um VIN, um número de estoque e um preço. A foto chega quando o fotógrafo do pátio chega, e para alguns veículos ela nunca chega: uma unidade em trânsito, um carro recebido na troca ainda em recondicionamento, um importado cujo nome de modelo não corresponde a nada no feed de imagens do DMS, um carro dos anos 1990 do qual ninguém licenciou uma foto de imprensa. Todas as telas que exibem esse registro — a lista do vendedor, a avaliação do comprador, o orçamento do cliente — mostram um espaço reservado cinza.

As ferramentas para concessionárias e importação na API preenchem esse espaço reservado com o que o registro já tem. Se ele tem um VIN, a imagem está a uma decodificação gratuita de distância. Se tem só uma linha numa planilha de estoque, a resolução por texto livre chega lá. De qualquer forma, o registro acaba guardando um id de veículo estável, e todas as telas renderizam o mesmo carro da mesma maneira.

Como é uma integração com concessionárias

  • Decodifique na entrada. O VIN é decodificado uma vez, quando o registro é criado, e o id de veículo do catálogo é armazenado ao lado dele. Nada nas etapas seguintes precisa escrever uma marca ou um modelo.
  • Uma vista para a lista, outra para o detalhe. Um side reto em uma caixa larga para listas de estoque; front-3-4 para a página de detalhes e o orçamento.
  • Anos antigos. O estoque de importados e usados remonta aos anos 1990 e é renderizado como os recém-chegados: o catálogo vai de 1990 até o ano-modelo atual, incluindo modelos do mercado doméstico japonês.
  • URLs assinadas dentro do app. O CRM é um navegador, então carrega URLs assinadas sem chave geradas pelo servidor, até cinquenta por chamada, renovando-as para registros que duram mais de uma semana.

Etapa 1: decodifique o VIN de graça

GET /api/v1/vin/{vin} não custa nada e exige apenas uma chave de API. Ele retorna o ano, a marca, o modelo, a versão, a classe de carroceria, o motor e todos os demais atributos decodificados que os dados vPIC da NHTSA conhecem e, quando o catálogo tem o veículo, um objeto vehicle com seu id estável e um image_path pronto. VINs parciais também funcionam: pelo menos cinco caracteres, * para cada posição desconhecida, além de uma dica opcional de year.

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

Passo 1b: quando tudo o que você tem é uma linha de uma planilha de estoque

O estoque importado costuma chegar como planilha: um ano, uma marca, um nome de modelo na grafia que a casa de leilões usou, um código de grade ou versão. POST /api/v1/images/resolve é gratuito e determinístico: extrai da frase as palavras de ano, cor e vista, busca no catálogo com o que sobra e retorna parâmetros exatos com um nível de confiança no qual você pode se basear.

Resolver uma linha da planilha de estoque
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 (resumido)
{
  "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…"
}

O veículo está certo (o Toyota Voxy 2019), mas a confiança é low, porque "ZS" é uma versão que o catálogo não conhece e a correspondência teve de contorná-la. Em uma importação em lote, trate high como resolvido e encaminhe medium e low para revisão humana com a lista candidates. Quando a planilha já tem o ano e a marca em colunas próprias, envie-os nos campos year e make e deixe só o modelo em query; a correspondência fica muito mais precisa. Uma frase sem palavras de veículo resulta em 400, e uma marca sem modelo correspondente resulta em 404, que lista os anos da marca para que você possa pedir o modelo.

Etapa 2: manter o id do veículo no registro

Seja qual for o caminho que o levou até lá, armazene vehicle.id (ou params.vehicle_id) no registro do estoque como uma coluna de string simples. Ele é determinístico e permanente: o mesmo veículo tem o mesmo id em todo deploy e após cada nova versão do catálogo, então é seguro usá-lo como chave estrangeira. A partir daí, toda solicitação de imagem é vehicle=veh_…, que não tem como ser digitada errado, e uma marca renomeada por um fabricante ou reclassificada por uma fonte nunca quebra uma URL armazenada.

Passo 3: uma vista para a lista, outra para a página de detalhes

Listas de estoque são largas e baixas, então a vista lateral reta em uma caixa 2:1 é a escolha comum: todas as unidades ficam voltadas para o mesmo lado e apoiadas na mesma linha de base. trim=1&padding=6 recorta no carro antes de ajustar, então uma Hilux e um Yaris preenchem a linha da mesma forma; fit=contain mantém a tela exatamente em 640×320 com margens transparentes. A página de detalhes usa front-3-4 em 1024, e as duas compartilham um cache: uma pintura que você não especificou sai prata em ambas.

A imagem de listagem de um registro
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

Etapa 4: exibir no CRM por meio de URLs assinadas

O CRM roda em um navegador, e um navegador nunca deve ter a chave de API. Quando uma tela lista uma página de estoque, o servidor gera uma URL assinada por registro com POST /api/v1/image-urls (até cinquenta por chamada; a imagem de cada URL conta como uma imagem de veículo do mês) e a página as carrega como qualquer imagem. Duas configurações importam para um registro que fica em estoque por meses:

  • ttl_seconds: 604800 com renew: true: a URL continua funcionando após o TTL de sete dias, e uma renovação conta a imagem uma vez no mês em que ocorre, e nada mais se ela já tiver sido contada naquele mês. Meses em que ninguém olha o carro não contam nada.
  • Uma Idempotency-Key por página da sincronização: uma requisição repetida reproduz a primeira resposta e nada é gerado duas vezes.

Restrinja a chave que gera as URLs à origem do CRM no painel, e uma URL que vazar para outro site responderá 403. Revogar a chave invalida todas as URLs que ela gerou. Se as imagens de veículo do mês do plano se esgotarem, uma renovação responde 402 e a mesma URL volta a funcionar quando a cota for renovada ou o plano for ampliado. O ciclo de vida completo está na documentação de URLs assinadas.

Cadastro de entrada e lista de estoque, com o SDK em 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(…), como acima.
  • MCP: decode_vin, resolve_vehicle, get_car_image e create_car_image_urls fazem parte do conjunto principal de ferramentas, então um assistente com o sistema de estoque no contexto consegue responder a "foto do estoque 4471" a partir do VIN do registro. A configuração está na página do MCP; os campos e erros do decodificador estão na documentação de VIN.

Perguntas frequentes

Quanto custa ter uma foto em cada registro de estoque?
A decodificação do VIN é gratuita. Cada veículo em cada pintura e vista é uma imagem de veículo, contada uma vez por mês, não importa quantas vezes seja exibida nem em que tamanho; verificá-la novamente com If-None-Match é gratuito. Uma URL assinada conta sua imagem quando é gerada e, com renew: true, mais uma vez em cada mês seguinte em que for aberta. Um grupo de concessionárias com alguns milhares de unidades em estoque exibe alguns milhares de imagens de veículo por mês; por isso, dimensione o plano de acordo com o estoque exibido.
O VIN é decodificado, mas vehicle vem null. E agora?
O decodificador identificou a marca, o modelo e o ano, mas o catálogo não tem uma entrada para essa combinação: um reboque, alguns chassis comerciais, um modelo que as fontes ainda não registraram. Mostre os campos decodificados, pesquise no catálogo com GET /api/v1/vehicles?q= caso a grafia seja diferente e solicite o veículo no quadro de solicitações; POST /api/v1/requests vota a favor de uma solicitação aberta em vez de duplicá-la.
A decodificação de VIN abrange importados e veículos nunca vendidos nos EUA?
O decodificador usa os dados do vPIC da NHTSA, então um VIN de um fabricante registrado na NHTSA é decodificado por completo. Um veículo nunca certificado para os EUA muitas vezes é decodificado apenas com fabricante e ano, sem modelo, e alguns nem chegam a ser decodificados. Para esse estoque, faça a identificação a partir do documento de registro: o catálogo em si é mundial, a partir de 1990, e inclui modelos do mercado doméstico japonês, como o Toyota Voxy.
A renderização mostra a versão do registro?
Não. O catálogo é indexado por marca, modelo e ano, então um cupê EX-V6 e um sedã LX são renderizados como o mesmo Accord 2003. Mantenha trim e body_class da decodificação como texto ao lado da imagem e nunca descreva a renderização como a unidade específica.
O site da concessionária pode usar as mesmas URLs que o CRM?
Gere-as a partir de uma chave cujas origens permitidas incluam o site; assim, uma URL carregada por um navegador a partir de qualquer outro site responde 403 origin_not_allowed. Carregamentos sem Referer, como os de um cliente de e-mail, são aceitos. O CRM e o site podem compartilhar uma chave e um conjunto de URLs, ou usar uma chave cada, para que qualquer um deles possa ser revogado isoladamente.