DEV Community

Vin Lookup
Vin Lookup

Posted on

Showing CabType from vPIC Without Inventing Crew-Cab Claims

NHTSA vPIC often returns a CabType field on DecodeVinValues-style payloads: catalog phrases such as "Crew/SuperCrew/Crew Max", "Extra/Super/Quad/Double/King/Extended", "Regular", or similar cab-oriented tokens. That string is useful on a free VIN decode card for pickups and chassis cabs. The trap is turning it into crew-cab marketing -- "seats six adults," "full rear doors," "family crew package" -- that the CabType field never asserted.

This post is about honest display: show CabType when vPIC provides it, refuse seating and door inventing, and allow a clean "not provided" state when the field is empty. Keep cab catalog labels separate from BedType, Doors, BodyClass, and brochure seating charts.

What CabType is (and is not)

CabType is a catalog attribute associated with the VIN decode. It answers a narrow question: which cab-type label did the decode associate with this pattern?

It does not answer:

  • Exact seat count or whether "six adults" fit comfortably
  • Whether rear doors are full-size, suicide, or absent
  • Bed length or payload (those are other fields or not provided)
  • Trim packages marketed as "Crew Cab Luxury"
  • Lifestyle copy ("soccer team ready," "whole crew fits") derived from Crew tokens

Empty CabType does not mean "Regular Cab" in a way that authorizes you to invent a seating pitch. It means vPIC did not give you that field. Do not fill the gap from BodyClass folklore or from a static cab chart keyed only by Make/Model/Year.

Normalize empties, do not invent seating

Treat blank, "Not Applicable", "N/A", and similar tokens as missing. Do not rewrite "Crew/SuperCrew/Crew Max" into "6-passenger" or append "full rear doors" because a brochure listed that combination for a different trim.

const EMPTY = new Set([
  "",
  "not applicable",
  "n/a",
  "na",
  "null",
  "none",
  "unknown",
]);

export type CabTypeView = {
  label: string | null;
  source: "vpic" | "missing";
};

export function viewCabType(fields: {
  CabType?: string | null;
}): CabTypeView {
  const raw = (fields.CabType ?? "").trim();
  if (!raw || EMPTY.has(raw.toLowerCase())) {
    return { label: null, source: "missing" };
  }
  // Keep the catalog token; do not coerce into seat / door claims.
  return { label: raw, source: "vpic" };
}

export function cabTypeLines(view: CabTypeView): string[] {
  if (view.source === "missing" || !view.label) {
    return ["Cab type: not provided by vPIC for this VIN"];
  }
  return [
    `Cab type (vPIC): ${view.label}`,
    "Catalog label only -- not a seat count or door configuration claim",
  ];
}
Enter fullscreen mode Exit fullscreen mode

The footnote matters. Buyers scanning a pickup decode will over-read any "Crew" token as a family-hauler guarantee. If vPIC said "Crew/SuperCrew/Crew Max", show that string with sourcing. If blank, say "not provided" -- no greyed "seats 5-6" placeholder.

Forbidden upgrades

Product pressure often asks for:

  1. Mapping CabType tokens into hard-coded seat counts (3, 5, 6) without a sourced seating field
  2. Inferring "full rear doors" or "suicide doors" from CabType alone
  3. Merging CabType with BedType into "Crew Cab Long Bed" when either field is missing
  4. Defaulting blank CabType to "Crew" so truck inventory UIs look premium
  5. Turning CabType into a family or job-site slogan

Refuse those. If you show BedType, Doors, or BodyClass from other sourced fields, show each on its own row. Never invent seating arithmetic in the mapper.

export function assertNoCabMarketing(moduleSource: string): void {
  const banned = [
    /seats?\s+\d/i,
    /\d+\s*[- ]?passenger/i,
    /full\s+rear\s+doors/i,
    /soccer\s+team/i,
    /family\s+crew\s+package/i,
    /whole\s+crew\s+fits/i,
  ];
  for (const re of banned) {
    if (re.test(moduleSource)) {
      throw new Error(
        `CabType display must not invent crew-cab claims: ${re}`,
      );
    }
  }
}

export function cardRows(fields: {
  CabType?: string | null;
  BedType?: string | null;
  Doors?: string | null;
  BodyClass?: string | null;
}): { label: string; value: string }[] {
  const cab = viewCabType(fields);
  const bed = (fields.BedType ?? "").trim();
  const doors = (fields.Doors ?? "").trim();
  const body = (fields.BodyClass ?? "").trim();
  const rows: { label: string; value: string }[] = [];

  if (cab.source === "vpic" && cab.label) {
    rows.push({ label: "Cab type (vPIC)", value: cab.label });
  } else {
    rows.push({ label: "Cab type (vPIC)", value: "not provided" });
  }

  if (bed && !EMPTY.has(bed.toLowerCase())) {
    rows.push({ label: "Bed type (vPIC)", value: bed });
  }
  if (doors && !EMPTY.has(doors.toLowerCase())) {
    rows.push({ label: "Doors (vPIC)", value: doors });
  }
  if (body && !EMPTY.has(body.toLowerCase())) {
    rows.push({ label: "Body class (vPIC)", value: body });
  }

  return rows;
}
Enter fullscreen mode Exit fullscreen mode

Separate rows keep attribution clear. A single "Crew package" chip that secretly combines CabType and Doors is how marketing sneaks past review.

When to hide the row entirely

You may always show "not provided" or omit the row on clearly non-pickup BodyClass values. Either is fine if you do not invent. Never fill a blank with a crew-cab upsell.

Small tests that lock honesty

import assert from "node:assert/strict";

assert.equal(
  viewCabType({ CabType: "Crew/SuperCrew/Crew Max" }).source,
  "vpic",
);
assert.equal(viewCabType({ CabType: "N/A" }).source, "missing");
assert.equal(viewCabType({ CabType: "" }).source, "missing");

assert.ok(
  cabTypeLines(
    viewCabType({ CabType: "Regular" }),
  ).some((l) => /not a seat count/i.test(l)),
);
assert.ok(
  !cabTypeLines(
    viewCabType({ CabType: "Crew/SuperCrew/Crew Max" }),
  ).some((l) => /seats \d|passenger|soccer/i.test(l)),
);

const rows = cardRows({
  CabType: "",
  BedType: "Long",
  Doors: "4",
  BodyClass: "Pickup",
});
assert.ok(
  rows.some((r) => r.label.includes("Cab type") && r.value === "not provided"),
);
assert.ok(rows.some((r) => r.label.includes("Bed type")));
assert.ok(rows.some((r) => r.label.includes("Doors")));
assert.ok(!rows.some((r) => /seats \d|Crew Cab Long/i.test(r.value)));
Enter fullscreen mode Exit fullscreen mode

Add a review rule: the CabType module must not contain seating or family-crew marketing phrases except in tests that forbid them. For chips, show the catalog type with a "Cab type" label -- never upgrade a blank into "Crew Cab."

Takeaway

CabType is a catalog label. Display it with clear sourcing, allow "not provided," and never use it as a key into invented seat counts or door claims. Your free VIN UI stays trustworthy when cab type is either a real vPIC value with a modest footnote -- or absent -- and crew-cab marketing lives somewhere else, clearly labeled, or not at all.

I maintain VIN Lookup, a free VIN decode based on NHTSA data.

Top comments (0)