Migration

How to migrate from Auto.dev to Cardog

Last updated:

Move from Auto.dev's VIN decode to Cardog's /v2/vin, with a field-by-field map and an adapter you can use while you switch.

Most teams moving from Auto.dev start with VIN decode. You replace GET https://api.auto.dev/vin/{vin} with GET https://api.cardog.app/v2/vin/{vin} and remap about a dozen fields. Year, make, model and trim carry over directly. Body, drive, transmission and plant country come back as entity refs that join to recalls, spec sheets and prices, and engine and plant details sit in the response's attributes map. You also gain Canadian VINs decoded to trim, Transport Canada recalls and batches of 1,000.

This guide is written by Cardog. Every claim about Auto.dev links to Auto.dev's own public documentation, and every claim was checked on 2026-10-26.

The short version

  1. Create a Cardog API key and send it as x-api-key. You can also send Authorization: Bearer crdg_..., the same header style Auto.dev recommends.
  2. Point your VIN decode at GET https://api.cardog.app/v2/vin/{vin} and pin Cardog-Version: 2026-10-21.
  3. Branch on valid instead of the HTTP status. When Cardog cannot name a VIN it returns 200 with valid: false and a reasons list, not a 404 or 207.
  4. If you want your existing Auto.dev-shaped code to keep running while you move, use the adapter below.
  5. Before you switch traffic, compare the two APIs on a sample of your own VINs.

How their fields map to ours

Auto.dev's fields come from the example response and the response structure on its Global VIN Decode page. Cardog's paths come from our response contract for GET /v2/vin/{vin}.

attributes is the decoder's attribute map for the build, keyed by the ids in our spec catalogue (GET /v2/specs/catalog). Each entry is an object, usually { value }, and a measured value can carry a unit. The map is sparse: an entry is there only where the decoder has that fact for the build.

Auto.dev fieldCardog fieldNote
vinvinSame.
vinValidvalidNot the same thing. Auto.dev's page lists vinValid as a boolean but does not define it. On Cardog, valid means the VIN decodes to a vehicle. A VIN that is not 17 valid characters gets a 400 with code invalid_vin on Cardog.
wminot mappedIt is VIN characters 1 to 3, so you can compute it yourself.
originrefs.countryFormat change: the plant country as a country ref, such as "country:canada" or "country:united-states-usa", not a display name. null when the decoder does not state a plant country.
squishVinnanoRenamed, format change. A ref with a nano: prefix. Auto.dev does not define squishVin, but in its example it is VIN positions 1 to 8, then 10 and 11. Cardog's nano uses the same positions for most makers. Low-volume makers whose WMI ends in 9 get a longer key. Cardog also has a field called squish. That is a different, shorter key, so do not map squishVin to it.
checkDigitnot mappedVIN character 9.
checksumnot mapped
typenot mapped
makemakeSame (display name). refs.make is the join key.
modelmodelSame (display name). refs.model is the join key.
trimtrimSame idea: the catalogue trim decoded from the VIN pattern. Can be null or absent.
stylenot mapped
bodyrefs.bodyStyleFormat change. A ref such as "body-style:pickup", not display text.
engineattributes.displacementL, attributes.cylinders, attributes.engineConfiguration, attributes.horsepowerFrom, attributes.engineModelNot one description string. Separate values on the VIN response where the decoder has them, and with units on the build sheet (sections.engine.* on GET /v2/specs/nano:{code}).
driverefs.driveTypeFormat change. A ref, not display text. Can be null.
transmissionrefs.transmissionFormat change. A ref, not display text. Can be null.
vehicle.vinvinSame value. Cardog has no nested vehicle object.
vehicle.yearyearMoved to the top level. Can be null.
vehicle.makemakeMoved to the top level.
vehicle.modelmodelMoved to the top level.
vehicle.manufacturerattributes.manufacturerWhere the decoder has it.
ambiguousnot mappedCardog returns the catalogue trim decoded from the VIN pattern.
trims, stylesnot mapped
yearAmbiguous, years, yearEncodednot mapped
reason, retryable (on a 207)reasonsDifferent model. Cardog answers 200 with valid: false and codes from a closed list (for example UNSEEN_BUILD, WMI_UNKNOWN, PATTERN_UNKNOWN).
nonerefs.make, refs.model, refs.modelYear, refs.fuelType, refs.electrificationLevel, refs.vehicleType, refs.gvwr, refs.series and othersCardog only. Stable keys that join to recalls, specs, prices and safety.
nonedoors, numberOfWheels, numberOfAxles, shippingWeight, grossWeightCardog only. Optional. Weights are in pounds.
nonesquish, corgiBuild, authorityTier, linksCardog only. links gives the paths to the recalls, listings, specs, safety and price for this VIN.
noneother attributes.* entriesCardog only, where the decoder has them: for example transmissionSpeeds, fuelSystem, plantCity, plantCompany, battery fields for EVs and ADAS entries such as adaptiveCruiseControl.

An adapter for the migration

This function calls Cardog and returns an object in Auto.dev's documented /vin/{vin} shape, for the fields that map. Fields with no mapping are left undefined with a comment. Use it to bridge your migration; it is code you own and run.

type AutoDevVin = {
  vin: string;
  vinValid?: boolean;
  wmi?: string;
  origin?: string;
  squishVin?: string;
  checkDigit?: string;
  checksum?: boolean;
  type?: string;
  make?: string;
  model?: string;
  trim?: string | null;
  style?: string | null;
  body?: string;
  engine?: string;
  drive?: string;
  transmission?: string;
  vehicle: { vin: string; year?: number; make?: string; model?: string; manufacturer?: string };
  ambiguous?: boolean;
  yearAmbiguous?: boolean;
};

export async function decodeVinAutoDevShape(vin: string, apiKey: string): Promise<AutoDevVin | null> {
  const res = await fetch(`https://api.cardog.app/v2/vin/${encodeURIComponent(vin)}`, {
    headers: { "x-api-key": apiKey, "Cardog-Version": "2026-10-21" },
  });
  if (res.status === 400) throw new Error("INVALID_VIN_FORMAT"); // Auto.dev would also 400
  if (!res.ok) throw new Error(`Cardog ${res.status}`);
  const c = await res.json();

  // Auto.dev answers 404 (or 207) here; Cardog answers 200 with valid:false and c.reasons.
  if (!c.valid) return null;

  const v = String(c.vin);
  return {
    vin: v,
    vinValid: undefined,      // Cardog's `valid` means "decodes"; Auto.dev does not define vinValid
    wmi: v.slice(0, 3),       // computed from the VIN, not returned by Cardog
    origin: undefined,        // Cardog returns a ref in c.refs.country, e.g. "country:canada"
    squishVin: v.slice(0, 8) + v.slice(9, 11), // matches Auto.dev's example, computed locally
    checkDigit: v[8],         // computed from the VIN, not returned by Cardog
    checksum: undefined,      // not mapped
    type: undefined,          // not mapped
    make: c.make ?? undefined,
    model: c.model ?? undefined,
    trim: c.trim ?? null,
    style: undefined,         // not mapped
    body: undefined,          // Cardog returns a ref in c.refs.bodyStyle
    engine: undefined,        // no description string; parts in c.attributes and on GET /v2/specs/{c.nano}
    drive: undefined,         // Cardog returns a ref in c.refs.driveType
    transmission: undefined,  // Cardog returns a ref in c.refs.transmission
    vehicle: {
      vin: v,
      year: c.year ?? undefined,
      make: c.make ?? undefined,
      model: c.model ?? undefined,
      manufacturer: c.attributes?.manufacturer?.value ?? undefined, // where the decoder has it
    },
    ambiguous: undefined,     // not mapped
    yearAmbiguous: undefined, // not mapped
  };
}

If your code reads body, drive or transmission, read the refs (c.refs.bodyStyle, c.refs.driveType, c.refs.transmission) directly. Do not turn them into display strings. The refs are stable keys, and a label you guessed would be a value neither API gave you.

Verify before you switch

  1. Pick 20 to 50 VINs from your own traffic: your common models, a few EVs, a pickup, anything built outside North America, and a VIN Auto.dev returned 207 or 404 for.
  2. Call both APIs for each VIN and save the raw JSON.
  3. Compare Auto.dev's vehicle.year, make, model and trim with Cardog's year, make, model and trim. Note any VIN where one side names the vehicle and the other does not.
  4. For every Cardog valid: false, read reasons. UNSEEN_BUILD means the build code is well formed but has not been decoded yet. You can report it at POST /v2/feedback, which is also in the response's links.feedback.
  5. Run your own downstream tests against the adapter output before moving production traffic.

What Cardog adds

  • Transport Canada recalls. GET /v2/vin/{vin}/recalls lists the Transport Canada and NHTSA campaigns for the VIN's model year. Each recall is tagged with its authority ("tc" or "nhtsa"). Auto.dev's Recalls API uses NHTSA data, plus international recalls matched by make and model. Its page does not mention Transport Canada (Vehicle Recalls).
  • Batch decode. POST /v2/vin/batch takes 1 to 1,000 VINs and returns one result per VIN, in request order. One bad VIN fails only its own row, never the whole batch.
  • No guessing on a miss. When we cannot name a VIN, every identity field and ref is null and reasons says why. We do not fill it in from similar vehicles.
  • Join keys. refs and nano link a decode to recalls, spec sheets, market prices and safety data without string matching. links gives you the paths.
  • US and Canadian spec sheets. GET /v2/specs/nano:{code} returns the build's spec sheet with a unit on every number. Pricing and colours are listed separately for each market, US and CA.
  • Weights and counts. doors, numberOfAxles, shippingWeight, grossWeight and a GVWR class ref on the decode.
  • Pinned versions. Send Cardog-Version to pin the API version you built against.

FAQ

Do I have to change my auth header? No. Cardog accepts Authorization: Bearer crdg_... as well as x-api-key.

What happens when Cardog cannot decode a VIN? You get a 200 with valid: false. The identity fields and refs are null, and reasons is not empty. If the VIN is not 17 valid characters, you get a 400 with code invalid_vin.

Why is body a ref like body-style:pickup? Refs are stable join keys across our API. Display text can change, but the ref does not.

Where is the engine description? There is no single string. The parts are in attributes on the decode (displacementL, cylinders, engineConfiguration, horsepowerFrom), where the decoder has them, and with units on the build sheet: take nano from the decode and call GET /v2/specs/{nano}, then read sections.engine.

Can I decode many VINs at once? Yes. POST /v2/vin/batch takes up to 1,000 VINs.

How is a decode billed? The identity route is priced at two credits per VIN. Today a single GET /v2/vin/{vin} bills those two credits for any 200 answer, including valid: false, while a batch bills only the rows that come back valid: true. A 400 costs nothing. See /pricing.

Sources

Checked on 2026-10-26:

If something here is wrong, tell us and we will fix it.