Changelog
What shipped, newest first. Biweekly.
The build code
August 13, 2026
Trim names can't identify a vehicle. "Work Truck" spans four weight classes; "LE" spans gas and hybrid. The VIN's own build pattern — positions 1–8 plus 10, the nano — separates them, and as of today it is a first-class key across the platform.
curl -H "Authorization: Bearer $CARDOG_KEY" \
"https://api.cardog.app/v2/vin/2T3RWRFV4MW112492"
# → "nano": "nano:2T3RWRFVMW",
# "links": { "specsBuild": "/v2/specs/nano:2T3RWRFVMW",
# "safetyBuild": "/v2/safety/nano:2T3RWRFVMW",
# "nanoEntity": "/v2/entities/nano:2T3RWRFVMW", ... }The nano is pure string math over the VIN — derivable offline with
@cardog/entities, stable forever, and now dereferenceable: 824,000 build
codes are registry entities, one per configuration the decoder has verified.
GET /v2/specs/nano:{code} — the config-exact sheet. The decoded
configuration (weight class, drive, fuel/hybrid family, engine — each with
source, confidence, and the decoder build that produced it) plus every
catalog entry that maps to this build. When several map (option packages
don't change the VIN pattern), you get them all and the fold of what they
agree on — never a silently-picked one.
GET /v2/safety/nano:{code} — the rating for THAT build. Ratings are
selected by joining the build's decoded axes against what NHTSA declared —
never by matching trim text. The response states its own narrowness:
configuration— exactly one rating survives: it's yours.tested: truemarks the rating whose crash car was literally this build.partial/model-year— the honest short list, or the full set. Blockers are named, never papered over.exempt— NCAP does not rate vehicles over 8,500 lb GVWR. A 2023 F-350 answers exempt, not "missing" — with the listed configurations and their driver-assist data still included.
Weight class, filled in. North-American catalog rows never carried GVWR
from source sheets; it is now derived from real decoded VINs,
unanimity-gated — 15,000 rows including every 2023 F-350 — so
/v2/specs/{model-year} serves the class your pricing model reads, and it
agrees with the decode.
Nothing breaks: model-year refs keep answering everywhere they did, and the new links ride alongside the old ones. The build code is additive — a sharper key for consumers who need configuration truth, which is anyone pricing, insuring, or rating an actual vehicle rather than a nameplate.
API 2.0
July 30, 2026
The Cardog API is now one keyed surface at api.cardog.app/v2. Resolve
free text to a permanent ref once, then dereference that ref into identity,
specs, listings, live market data, and recalls behind a single key.
curl -H "Authorization: Bearer $CARDOG_KEY" \
"https://api.cardog.app/v2/entities/resolve?q=tesla+model+y"
# → { "ref": "model:tesla/model-y", ... }
curl -H "Authorization: Bearer $CARDOG_KEY" \
"https://api.cardog.app/v2/listings?make=make:tesla&year=2024"model:tesla/model-y is a permanent join key. Store it in a database
column, a config file, or an agent's memory file; it will mean the same
vehicle for as long as the platform exists.
Every non-2xx response carries the fix. The envelope is
{ code, message, hint, docs_url, suggestions }. An unknown ref returns a
400 that names the offending ref and offers the nearest valid ones — we
never silently fuzzy-match a typo into a different vehicle. A wrong answer
that looks right is worse than an error, so you get the error and the
correction in the same response.
The contract generates the surface. Schemas in @cardog/contracts
validate responses, not just requests, and every downstream artifact
regenerates from them: the OpenAPI spec, the docs reference, the conformance
table, the live-check suite, both SDKs' types, and the MCP tool layer.
Nothing user-facing is hand-maintained, so nothing user-facing can drift.
Nine route groups, all ref-native:
| Group | What it answers |
|---|---|
entities | free text → permanent refs; the only door free text enters |
vin | VIN → identity, with per-item batch |
specs | 155-attribute catalog by trim |
listings | ref-native search, facets, count over live Canadian listings |
instruments · quotes · tape | live Canadian market data |
recalls | Transport Canada + NHTSA, fused, ref-keyed |
pricing | machine-readable rate card at GET /v2/pricing |
Docs are generated, and readable by machines.
cardog.app/docs has zero hand-written endpoint
prose — cURL / TypeScript / Python tabs come from both SDK method maps, ⌘K
search, and a try-it panel that runs against your own key. Every page has a
.md twin, and cardog.app/docs.md is the
whole platform in one fetch.
@cardog/entities is on npm, Apache-2.0. The ref grammar as an open
package: validate, build, and type refs offline, forever, with zero API
calls and zero dependencies. The grammar is free; resolution and the facts
behind the refs are the product.
import { modelYearRef, parseRef, squishFromVin } from "@cardog/entities";
modelYearRef("model:tesla/model-y", 2024); // "model-year:tesla/model-y/2024"
squishFromVin("5TDGSKFC8RS123456"); // "5TDGSKFCR" — offline, no networkSDKs and MCP. @cardog/api (TypeScript) and cardog (PyPI) are
siblings generated from the same OpenAPI artifact, with envelope-typed
errors. mcp.cardog.io exposes five job-shaped tools —
resolve_entity, identify_vehicle, search_inventory,
market_quote, check_recalls — whose descriptions teach the ref
grammar inline.
Credits are visible on every call. X-Credits-Rate,
X-Credits-Remaining, X-Credits-Allowance, and X-Credits-Reset on
every keyed response; X-Credits-Source on every debited one. The rate
card is machine-readable at GET /v2/pricing. The free tier stops at 50
credits with an insufficient_credits envelope that says how to continue.
Paid tiers bill overage and are never blocked.
What changed for existing users
- v1 still works. No v1 route has been removed and none is being removed today. A deprecation window will be announced with dates in a future entry, not inferred from this one.
- Your existing key works on
/v2unchanged. One channel gate covers API key, session, and internal callers. - Free text now enters at one door. v1 accepted make/model strings on
several endpoints. In v2, free text goes to
GET /v2/entities/resolveand every other route takes refs. If you send a string where a ref belongs, you get a 400 naming the field and pointing at resolve — the migration instruction arrives in the response. - Errors changed shape. v1 error bodies were not uniform. v2 is always
{ code, message, hint, docs_url, suggestions }. Dispatch oncode; it is an open set, so fall back tomessagefor codes you do not know. - Nulls are honest.
nullmeans not-derivable from the evidence. It never means unknown, and it is never a placeholder.
Improvements
- Docs content negotiation:
Accept: text/markdownon any docs page, or append.md. - Conformance table and live-check suite generated from the contract and run per-PR against a preview deployment.
Next
Named because they are being built, not because they are available: resolve
that survives messier real-world queries, agent-minted trial keys, recall
watches, ?include= so one call answers one job, a self-serve portal for
metering and upgrades, and idempotency keys.
Full reference: cardog.app/docs · one fetch: cardog.app/docs.md · this changelog as markdown: cardog.app/changelog.md