SDKs & MCP

Ref grammar

Last updated:

Validate, build, and type refs offline with @cardog/entities.

Every identifier in the Vehicle Graph, from make:honda to model-year:honda/civic/2021 to squish:5TDGSKFCR, follows one grammar, and that grammar is published as an open-source package. Install it and your code can validate, build, parse, and type refs offline: no API key, no network call, no rate limit.

npm install @cardog/entities

ESM, TypeScript-first, zero dependencies, Apache-2.0. Runs in Node, browsers, workers, and edge runtimes.

Validate and build, offline

import {
  isRef, isRefOf, buildRef, parseRef,
  modelYearRef, parseModelYearRef, recallRef,
} from "@cardog/entities";

isRef("make:tesla");                        // true
isRef("make:Tesla");                        // false: casing is part of the grammar
isRefOf(input, "model-year");               // narrows to EntityRef<"model-year">

modelYearRef("model:tesla/model-y", 2024);  // "model-year:tesla/model-y/2024"
parseModelYearRef("model-year:honda/civic/2021");
// { make: "make:honda", model: "model:honda/civic", year: 2021 }

recallRef("nhtsa", "23V123");               // "recall:nhtsa/23v123"

Every builder validates against the grammar and throws rather than normalizing, which is the same rule the API applies at its boundary. Use it to validate user input before it costs a call, to reject a malformed ref in a form handler, or to assert grammar in your tests.

The VIN grains, offline

nano and squish, the grains between one VIN and a whole model year, are pure functions of the VIN, so the package derives them locally:

import { squishFromVin, nanoFromVin, squishRef, nanoRef } from "@cardog/entities";

squishFromVin("5TDGSKFC8RS123456");  // "5TDGSKFCR": the exact-configuration market grain
nanoFromVin("5TDGSKFC8RS123456");    // "5TDGSKFC*RS": the comparables grain
squishRef("5TDGSKFCR");              // "squish:5TDGSKFCR": ready for /v2/quotes

A VIN in your database becomes a market-grain ref without touching the network. The API call you then make is the one that quotes it.

Typed unions

The enumerable domains ship as TypeScript unions, so a misspelled ref in a fixed vocabulary fails at compile time rather than at request time:

import type { FuelTypeRef, BodyStyleRef, CountryRef, EntityDomain } from "@cardog/entities/types";
import type { SpecAttributeId } from "@cardog/entities";

const fuel: FuelTypeRef = "fuel-type:electric";  // ✓ checked by tsc
const made: CountryRef = "country:canada";       // every ISO 3166-1 country
const attr: SpecAttributeId = "curbWeight";       // the 155-attribute catalog, typed

The open-ended domains (make, model) are typed structurally as EntityRef<"make">. Their vocabularies live in the registry, and free text reaches them through resolve.

The package also carries the spec attribute catalog (@cardog/entities/spec), the same catalog GET /v2/specs/catalog serves. Use it to build filter UIs and to validate spec.{attributeId} query keys before sending them.

The stability contract

Refs are permanent join keys, and the package states that in a form you can check:

  • GRAMMAR_VERSION is 1. It bumps only for a breaking change to the grammar itself, which the platform treats as something that should never happen. New domains and entities are additive.
  • vocabulary carries the provenance of the build the package was generated from, so what your process validates against is auditable.
  • A ref, once issued, keeps its meaning for the life of the platform. Store refs in your columns and your config.

Where the API begins

The package contains no entity data (no make list, no model vocabulary, no display names) and no VIN decoding. Holding the grammar tells you a ref is well-formed. Only the registry can tell you what it names:

# free text → refs (the only route that accepts unstructured input)
curl "https://api.cardog.app/v2/entities/resolve?q=2021%20civic" -H "x-api-key: $CARDOG_API_KEY" -H "Cardog-Version: 2026-10-26"

# ref → the entity: names, hierarchy, counts, links into every group
curl "https://api.cardog.app/v2/entities/model:honda%2Fcivic" -H "x-api-key: $CARDOG_API_KEY" -H "Cardog-Version: 2026-10-26"

# VIN → identity as refs
curl "https://api.cardog.app/v2/vin/5TDGSKFC8RS123456" -H "x-api-key: $CARDOG_API_KEY" -H "Cardog-Version: 2026-10-26"

The division of labour is simple. Validate and construct refs locally. When a question needs the registry (resolution, enumeration, specs, recalls, quotes), call /v2/entities and its sibling groups.