> ## Documentation Index
> Fetch the complete documentation index at: https://lmn.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# History lookups — generation 2 (legacy)

> Legacy generation-2 history lookup envelope: report, aggregate, sources, export and safety. New integrations should use the raw-facts format.

> [!WARNING]
>
> **Legacy format.** This page documents generation 2, the provider-shaped envelope (`report`, `aggregate`, `sources`, `export`, `safety`). New integrations should use the [raw-facts format](https://lmn.mintlify.site/endpoints/history-lookups.md), which is the primary History response. Generation 1 (the original single-report response, before `aggregate` and `sources`) is retired for new integrations; stored generation-1 events keep their original body.

Use the History API to request an English vehicle-history result. In the expanded workflow, one lookup combines KIDI insurance history, national maintenance records, and dealer-listing evidence when the vehicle can be identified.

> [!NOTE]
>
> **Prepared for coordinated rollout; not an activation announcement.** The version-2 response contract and expanded collection are enabled separately per environment by LMN. Confirm your environment's access before integration testing. Existing HTTP responses may retain the earlier shape until cutover; stored historical webhooks retain their original shape afterward.

For a runnable client, complete response downloads, and receiver examples, see [History integration](https://lmn.mintlify.site/history-integration.md). The [schema reference](https://lmn.mintlify.site/schemas.md#vehicle-history-lookups) describes the consolidated fields.

## Routes

The routes, request fields, idempotency, deadline, retry and credit rules below are shared by every generation; only the response shape differs. The [raw-facts page](https://lmn.mintlify.site/endpoints/history-lookups.md) is the primary reference for them.

| Route | Behavior |
| - | - |
| `POST /v1/history-lookups` | Submit exactly one VIN or Korean plate. New work returns `202`; an in-flight VIN returns the same lookup ID and `202`. Plate idempotency replay returns `200`. |
| `GET /v1/history-lookups/{id}` | Read your lookup, including its terminal result. A retrieved failed lookup still returns HTTP `200`. |
| `GET /v1/history-lookups` | List your lookups newest first; filter by `status`, `created_from`, and `created_to`, and paginate with `cursor`. |

There is no separate partner lookup endpoint for the supplemental sources. Access and data are scoped to your organization and API environment.

## Submit and follow a lookup

1. Send exactly one `vin` or `plate`. A VIN is 17 characters, without I, O, or Q. Plate requests require an `Idempotency-Key`; VIN requests accept and ignore that header.
2. Save the returned `id`. A repeated in-flight VIN uses the same job and creates no additional webhook.
3. Poll that ID until `completed` or `failed`, or consume the signed terminal webhook on your existing endpoint.
4. Read `report` for insurance history, `aggregate` for consolidated facts, `export` for the export reporting date, and `safety` for model crash-test results. Inspect `sources` for coverage. A failed primary lookup can still contain supplemental facts and export evidence.

```bash theme={null}
curl -X POST "$LMN_BASE_URL/v1/history-lookups" \
  -H "x-api-key: $LMN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"vin":"KNAPM813BHK185630"}'
```

The VIN above selects the saved real Sportage example in sandbox, without provider calls or credits. It does not simulate current national maintenance record or dealer-listing collection. For production, use a verified vehicle identifier and your separately enabled production environment.

### Request fields

| Input | Rules |
| - | - |
| `vin` | Supply instead of `plate`. Normalizes to 17 uppercase characters, without I/O/Q and with at least one letter. Whitespace is removed; raw input is limited to 32 characters. |
| `plate` | Supply instead of `vin`. Modern or region-prefixed Korean registration plate; whitespace and hyphens are normalized, with raw input limited to 32 characters. |
| `reference` | Legacy envelope responses only. Not supported once raw-facts responses are enabled for your organization: a new request containing it returns `400 VALIDATION_ERROR` (`details.field: "reference"`). A replay of an earlier plate idempotency key with its identical original body, including its original `reference`, still returns the stored lookup. Where accepted: optional nonempty printable ASCII string, at most 128 characters; a correlation value, not an idempotency key or unique lookup constraint. |
| `fresh` | Optional boolean, default false. Authorized new plate requests can bypass source reuse; partner VIN requests cannot authorize another paid refresh through this flag. |
| `Idempotency-Key` header | Required for plate requests, at most 255 characters. Ignored on VIN requests. |

Unknown JSON properties and requests containing both or neither identifier return HTTP400. The same normalized plate body treats omitted `fresh` as false; changing `fresh` (or, on legacy envelope requests, `reference`) with the same key is a different body.

### VIN reuse and plate idempotency

When LMN can link a plate to its VIN from stored registry evidence or inventory, plate and VIN requests for the same vehicle follow the same reuse and credit rules below. A plate that cannot be linked is handled as a new plate request.

In the expanded live workflow, a request after a VIN's earlier lookup has finished creates a new asynchronous lookup. Each provider can reuse its eligible seven-day source capture; the partner's active 30-day credit entitlement determines whether the result is free. An earlier frozen lookup is not returned as a new consolidated check. Before expanded collection is enabled, the earlier workflow can return HTTP `200` for an eligible completed same-partner VIN lookup within its legacy 30-day reuse window.

For a plate, the same idempotency key and normalized body returns the existing lookup with `200`, even if it is still pending. The same key with a different body returns `422 idempotency_key_reused`. Use a new key for an intentionally new plate check. Replaying an old key reads that original job; it does not refresh its frozen result.

`fresh: true` can bypass provider-cache reuse for an authorized new plate lookup. On a VIN request it does not authorize another paid provider refresh. A reused job's credit fields, source, reference, and timestamps describe that original job, not a new charge.

## Collection, deadline, and availability

When the national vehicle registry is enabled, LMN collects registry evidence from the supplied VIN or plate before plate-dependent sources start. Verified registry evidence can link the two identifiers. Without it, LMN uses verified inventory mappings or the insurance-history response. Once a verified VIN and plate are available, the insurance history, national maintenance records, and dealer-listing sources can run in parallel. The VIN-based national export records lookup runs independently of plate resolution.

National maintenance records need a plate; the separate national export records lookup uses the submitted VIN. Dealer-listing collection searches by the verified plate and can retrieve listing history, nested history details, insurance, and inspection evidence. A missing listing or unavailable source does not erase facts ready from another source. A plate or VIN echoed by a request alone is not vehicle identity evidence.

The content deadline for national records, dealer listings, export, and safety is **five minutes from lookup acceptance**. LMN freezes the facts ready by that deadline, or earlier when all sources have finished. Results that become ready afterward do not change that lookup's HTTP result or terminal event. Webhook delivery and finalization can occur later; five minutes is a content cutoff, not a network-delivery SLA.

When the insurance-history source is temporarily unavailable, LMN retries it with backoff for up to about 15 minutes after the first unavailable attempt. The lookup stays `processing` and shows a `retry` object with `next_attempt_at` and `window_ends_at`. Supplemental facts keep their five-minute cutoff. If the insurance-history report becomes ready, the lookup completes and sends the normal `history_lookup.completed` webhook; otherwise it fails once, with no credit charged. A report LMN has already obtained for the lookup is still delivered even if processing finishes after the window. A failed lookup is never reopened.

| Primary insurance-history report | Supplemental result | Terminal status and credits |
| - | - | - |
| Available | All, some, or no supplemental facts | `completed`; existing successful-lookup credit rules apply. |
| Unavailable by cutoff | National record or dealer-listing facts available | `failed`, `report: null`, available `aggregate`; no credit charged. |
| Unavailable by cutoff | No publishable facts | `failed`, `report: null`, `aggregate: null`; no credit charged. |

Supplemental collection does not add a second or third history credit.

## Version-2 response

The lookup object is returned directly by POST and GET-one. GET-list uses `{data: [...], next_cursor: ...}`. New terminal webhook events place the lookup object in `data`.

| Field | Meaning |
| - | - |
| `history_lookup_schema_version` | `2` after environment cutover. Missing on a stored legacy event means the earlier contract. |
| `id`, `status` | Polling ID and `queued`, `processing`, `completed`, or `failed`. |
| `query` | Original normalized `plate` / `vin`, successful `key_used`, and `fallback_plate_from_inventory`. Request identifiers can remain null even when the aggregate has a verified binding. |
| `identity_check`, `identity_check_basis` | Existing primary-report identity result and matching attribute list; null before completion or on failure. A verified aggregate binding is separate from a source report's missing VIN. |
| `reference` | Original accepted request's ASCII reference, or null. |
| `source`, `report_as_of` | Primary insurance-history delivery: `fresh` / `cache` and its original capture time; null without a primary report. These do not describe all providers. |
| `report` | Existing normalized insurance-history (KIDI) report with its own `schema_version: 1`; null until completed, and null on failure. |
| `aggregate` | Consolidated, attributed facts and events with its own `schema_version: 1`; may be non-null even on failure. |
| `sources` | One entry for each selected source, under its legacy provider key (`carhistory`, `car365`, `encar`, and `ts` when enabled); coverage, neutral reason, revision ID, and original capture time. |
| `export` | VIN-native national export records result. `reporting_date` is the official export permit/reporting date (`수출이행 신고일`), not deregistration, shipment, or vessel departure. `available` includes a VIN-matched date and `captured_at`. `no_record` means the source returned no matching record at capture time; it does not prove the vehicle was never exported. `unavailable` means the lookup could not provide usable evidence; `pending` and `collecting` are in progress, and `not_collected` means no export lookup was scheduled. The date is null unless the status is `available`. |
| `failure` | On terminal failure: `code`, `message`, and optional `details`. Use the code for logic. |
| `billable`, `credits_charged` | Production charge indicator and persisted deduction (`0` or `1`). Sandbox always has `billable: false`; real-data trials can consume test credits. |
| `retry` | While a `processing` lookup waits for a temporarily unavailable insurance-history source: `state: "retrying"`, `reason: "provider_unavailable"`, `next_attempt_at` (null until scheduled), and `window_ends_at`, after which no new attempt starts. Null otherwise, and always null on `completed` or `failed` lookups. |
| `created_at`, `completed_at` | Acceptance and terminal processing times, with timezone offsets; completion is null while pending. |

The outer contract version, primary report version, and aggregate version are independent. An outer version of 2 does not change the primary report to version 2.

### Complete response examples

Every example below is a complete JSON object generated offline through the response mapper and consolidation code. The linked pages include full response bodies and JSON downloads. Identifiers, observations, times, and costs are illustrative synthetic data, not a current vehicle report.

| Example | What to handle |
| - | - |
| [Queued](https://lmn.mintlify.site/history-response-examples/queued.md) | Pending source entries; report and aggregate null. |
| [Completed](https://lmn.mintlify.site/history-response-examples/completed.md) | Primary report plus consolidated facts, an event, and provenance from all three providers. |
| [Completed with partial coverage](https://lmn.mintlify.site/history-response-examples/completed-partial.md) | Keep the primary report and available facts when another provider is partial or has no listing. |
| [Failed with supplemental data](https://lmn.mintlify.site/history-response-examples/failed-supplemental.md) | Preserve aggregate despite failed status; no credit charged. |
| [Failed without usable data](https://lmn.mintlify.site/history-response-examples/failed-unavailable.md) | Both report and aggregate null; inspect source reasons. |
| [List](https://lmn.mintlify.site/history-response-examples/list.md) | Paginated wrapper around lookup objects. |
| [Completed webhook](https://lmn.mintlify.site/history-response-examples/completed-webhook.md) | Full terminal event, with the lookup under `data`. |
| [Failed webhook](https://lmn.mintlify.site/history-response-examples/failed-webhook.md) | Failed event retains the supplemental aggregate. |
| [Legacy webhook](https://lmn.mintlify.site/history-response-examples/legacy-completed-webhook.md) | Illustrative stored earlier-generation event, without v2-only fields. |

### Source availability

| `sources[].status` | Meaning |
| - | - |
| `pending` | This lookup's provider outcome is not frozen yet. |
| `available` | A ready provider report was admitted to this lookup. |
| `partial` | Some ready facts are included, with incomplete provider readiness. |
| `unavailable` | No ready facts from this provider are included. |

Neutral `reason` values are `not_collected`, `plate_unresolved`, `identity_unresolved`, `no_listing`, `no_record`, `provider_unavailable`, `report_unavailable`, and `source_translation_incomplete`, or null. Reasons do not contain internal processing diagnostics. `source_translation_incomplete` (status `partial`): the insurance history was delivered; one or more verbatim source blocks could not be translated and are omitted from `report.source_sections`. Structured fields are complete. The omitted locations are listed in the reconciliation coverage manifest as `completeness.gaps` entries `{kind:"source_translation", section_id, scope:"title"|"block"|"section", block_index?, block_kind?:"text"|"table", reason:"untranslatable_source_text"|"translation_unavailable"}` (no source text). `translation_unavailable`: translation was temporarily unavailable; a later new lookup of the same vehicle may return the block. A request answered from an earlier lookup returns that lookup's result unchanged. A source with no admitted revision has null `revision_id` and `captured_at`.

Old jobs and ordinary sandbox fixtures projected after cutover can have `not_collected` for supplemental providers. A version-2 HTTP envelope does not assert that those old jobs were recollected. For a reused capture, `captured_at` keeps its original time.

## Reading the combined report

One lookup returns four complementary sections. GET, list items, and new terminal webhooks share the same response structure.

| Section | What to use it for | How to read missing data |
| - | - | - |
| `report` | KIDI insurance history. | Null until a successful primary report is available. |
| `aggregate` | Vehicle identity, attributed facts, and events from the collected sources, including national vehicle registry and mileage observations when enabled. | Check `sources` and each fact's provenance; one unavailable source does not erase other evidence. |
| `export` | National export records reporting date for the submitted VIN. | Read `status`; `no_record` and `unavailable` have different meanings. |
| `safety` | Model-level crash-test ratings and images. | Read each result's applicability. `reference_only` does not establish a rating for this specific vehicle. |

The `report` object keeps its existing insurance schema. National vehicle registry facts, export dates, and model safety results retain their own provenance and availability in the surrounding lookup response. A completed lookup means the primary report completed; it does not mean every supplemental source returned data. A failed lookup can still contain `aggregate` and `export` evidence; `safety` remains null on failure.

## Reading consolidated data

The aggregate has `vehicle`, `facts[]`, `events[]`, and `complete`. See [typed schema details](https://lmn.mintlify.site/schemas.md#vehicle-history-lookups).

* Each fact has a `key`, `subject`, `cardinality`, `values[]`, and `conflict`. A value carries its own source evidence.
* Equal values can share evidence from several providers. A scalar conflict retains all reported alternatives; do not silently choose the first or add them together.
* Work items, work categories, and part codes use set cardinality. Multiple set members do not themselves mean conflict.
* Verified matching events can be consolidated. Uncertain same-date or coarse-date matches remain separate with `certainty: "unresolved"` and a shared `candidate_group_id`. Do not count those as confirmed separate incidents or collapse them by date alone.
* Event `contexts[]` preserve date precision and mileage per source. Unknown context dates can be empty strings with precision `unknown`; do not invent a day from a month.
* Evidence identifiers are opaque. Use `provider`, `revision_id`, and `observation_id` for attribution; they are not URLs or pointers to a raw-data download.
* `aggregate.complete` describes reconciliation of admitted observations. It does not prove that every provider was available or that the vehicle's full lifetime history is known. Read `sources` as well.

Unknown values stay unknown. A missing fact is not zero, false, or a clean history. Only partner-ready English facts are published; unavailable content does not expose internal processing failures. Public registration identifiers can retain Korean plate characters.

## Primary insurance-history report

`report.schema_version` remains `1`. Its typed fields include:

| Field | Contents |
| - | - |
| `vehicle` | Manufacturer, model, trim, year, fuel, displacement, body/use class, first insurance date, plate and VIN. Source-missing VIN can remain null despite a separately verified aggregate binding. `fuel` is `gas` when the source reports a gaseous fuel without saying which: it can be LPG or CNG. |
| `summary`, `special_use`, `special_accidents` | Reported claim counts, totals, pending estimates, owner/plate changes, total-loss/theft/flood counts, and rental/commercial/government use. |
| `ownership_history[]` | Date, kind, masked plate and use class. Kinds include owner/plate/use changes, first registration, and information-provision end. |
| `own_vehicle_damage[]`, `other_vehicle_damage[]` | Claim date, payer, pending indicator, estimate, payout, parts/labor/paint amounts. Own damage is damage to this car; other damage is damage this car caused to another. |
| `uninsured_periods[]` | Original uninsured month bounds. |
| `mileage`, `adas[]`, `value_range` | Recorded odometer history, source ADAS information, and an insurance-reference value range. The value range is not a market price. |
| `repair_details[]` | Repair date and categories of repaired, replaced or painted parts. |
| `source_sections[]` | Ordered English text and display tables, source explanations and limitations, and available repair-diagram content. Table cells are display strings; use typed fields for calculations. |
| `source` | Provider, report inquiry date, original LMN retrieval time and disclaimer. The retrieval time is not a timestamp asserted by the source. |

Null means unavailable or unstated. An empty array means the source section was present with no entries. Full dates use `YYYY-MM-DD`, months use `YYYY-MM`, and primary report `*_krw` amounts are Korean won integers without FX conversion. Source totals are retained rather than recalculated from list lengths.

## Safety ratings and crash-test images

When safety enrichment is enabled, a newly completed lookup includes a top-level `safety` object alongside `report`. It contains model-level safety ratings and official crash-test images, with the tested specification, source, capture time, and applicability stated for each result. This section is separate from insurance claims, listing photos, and the provider availability in `sources`.

| Field | Meaning |
| - | - |
| `scope` | Always `model_level`. Crash-test images depict the tested model and specification. |
| `status` | `available`: at least one verified applicability result; `reference_only`: contextual results only; `unavailable`: no supported result. Read each result's applicability separately. |
| `synthetic` | `true` identifies a sandbox example. |
| `reason` | The reason for an unavailable result, such as `unresolved_identity`, `identity_conflict`, `no_supported_mapping`, or `source_unavailable`; otherwise `null`. |
| `results[]` | Program and source record, tested vehicle, applicability and evidence, category ratings, image metadata, attribution, and provenance. |

U.S. government 5-Star Safety Ratings results use native five-star ratings for overall, frontal, side-impact, side-pole, and rollover categories where supplied, including available driver/passenger breakdowns. An unrated category has `value: null` and `status: "not_rated"`. `safety_concerns: null` means the source did not supply that information. Categories can have ratings without images. Missing data does not imply a zero-star result.

U.S. results describe U.S. specifications. A similarly named Korean-market model does not establish equivalent equipment or applicability. Unsupported mappings return `unavailable`; reference-only results list the unknown or differing attributes. Korean government new-car safety rating results remain unavailable until detailed source evidence and applicability are verified. These are model-level references and do not describe this VIN's accidents, equipment, or outstanding recalls.

Images use official externally hosted URLs in `media[].source_url`; `delivery_url` is currently `null`. Preserve source attribution and applicability context when displaying them, and follow each result's `usage_terms_url`. Image URLs can become unavailable independently of a rating. Video delivery is not included in this slice.

Pending, failed, pre-feature, and enrichment-disabled lookups return `safety: null`. Safety availability does not change insurance completion or credit charges. GET, list items, and newly generated webhooks use the same persisted safety snapshot. Reusing an existing lookup keeps its original snapshot; it does not refresh the catalog or generate an additional webhook. Stored historical webhooks retain their original shape.

With sandbox safety enabled, these synthetic inputs exercise the result states without external requests:

| Plate | VIN | Safety status |
| - | - | - |
| `00가1700` | `LMNSBX00000001700` | `available` |
| `00가1701` | `LMNSBX00000001701` | `reference_only` |
| `00가1702` | `LMNSBX00000001702` | `unavailable` |

All three examples are marked `synthetic: true`. Their applicability is invented for integration testing and is not evidence about an actual VIN.

## Seven-day captures and 30-day credits

| Rule | Behavior |
| - | - |
| Source reuse | Each source selects the newest capture first, then a compatible processing revision, before applying readiness and a strict age of less than seven days. Exactly seven days is expired. |
| Partial newest capture | It is not silently replaced by an older complete report. Ready facts can be included, or the available capture can be processed again without changing its original age. |
| Partner entitlement | A successful chargeable lookup grants that partner and environment a 30-day free-repeat entitlement for the verified VIN. Free repeats do not extend it. |
| Same data, different partner | A successful chargeable lookup uses that partner's credit even when every provider result comes from shared captures. |
| Supplemental sources | National records and dealer listings add no separate credit charge. |
| Failed primary | No credit is deducted, including when supplemental facts are returned. |
| Retry / replay | Replaying a job or delivering its terminal event again does not deduct another credit. A genuinely new lookup is evaluated under the existing entitlement rules. |

Credits are separate per organization and environment. A chargeable lookup reserves a credit while processing and settles once; a free-repeat entitlement remains usable at zero available balance. New chargeable work with no available credit returns `402 insufficient_history_credits`. A plate lookup needs a verified full VIN before a credit can be charged.

## Webhooks and compatibility

A terminal result creates one `history_lookup.completed` or `history_lookup.failed` event for the lookup. Retries can deliver that same event more than once: verify the [signature](https://lmn.mintlify.site/webhooks/signing-and-retries.md) and handle the event ID idempotently. Reusing an existing job sends no additional event.

New events use the environment's contract at finalization. Stored legacy event bodies and explicit replays remain unchanged after cutover, even when a current GET of that lookup uses a version-2 projection. Read `data.history_lookup_schema_version`; absence means legacy. The current GET is not a byte-for-byte replacement for a historical event.

New lookup responses and new terminal events have no PDF field. Use JSON rendering. Previously stored PDF links, where an authorized legacy object still exists, retain only their legacy access behavior; they do not provide PDFs for new lookups.

## Errors and limits

A successful GET of a failed lookup returns `200` with `status: "failed"`. Processing codes are `no_record`, `provider_unavailable`, and `provider_error`. A `no_record` response does not identify the reason the primary source has no report; inspect supplemental data independently.

HTTP errors are separate: `400` invalid input/missing plate idempotency key, `401` authentication, `403 history_lookup_not_enabled` for disabled partner access, `404 history_lookup_not_found` for an unavailable scoped ID, `422 idempotency_key_reused`, `402 insufficient_history_credits`, `429` rate/capacity limits, and `503 service_unavailable` for environment availability. See [Errors](https://lmn.mintlify.site/errors.md).

Default partner limits are 10 in flight and 200 per KST day; configured limits can differ. Honor `Retry-After`. A transport timeout is not proof that submission failed. With a returned ID, resume GET. If the POST response was lost, a plate's same idempotency key/body recovers its original job. A VIN repeat recovers the same job only while it is in flight; after completion, expanded collection creates a new lookup. Use GET-list with the identifier and time range (and, for legacy envelope requests, your accepted reference) to locate an original VIN job before intentionally submitting again.

## Sandbox and rollout

The saved Sportage VIN `KNAPM813BHK185630` remains a free historical sandbox example, even with `fresh: true`, and retains its original capture time and provider inquiry date. It is exempt from ordinary report-age reuse for that exact sandbox sample; it is not a current car check or proof of current multi-provider collection.

Ordinary fixture-mode sandbox inputs include `00가0000` (clean), `00가0001` (damage history), `00가0002` (sparse), `00가0404` (no record), and `00가0503` (unavailable). Real-data trial mode uses live provider behavior for other identifiers and can consume test credits. Confirm your mode before using a real plate or VIN. The downloadable multi-provider examples are offline contract fixtures; they do not claim the sandbox collected those three providers.

Environment cutover is coordinated in place on `/v1/history-lookups`; this is not a new `/v2` endpoint. Current HTTP/list projections and newly created events move to the v2 envelope, while old event replay stays original. Allow added fields, retain primary report version 1 parsing, handle absent v2 fields in legacy events, and remove reliance on a history PDF. LMN enables expanded collection only after its separate release checks.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.