v1.0.
v1.42 — 2026-06-22
Order resource and webhookorder.* payloads now include a pricing breakdown.
The Order resource returned by POST /v1/orders, GET /v1/orders/{id}, and GET /v1/orders (list) now carries a top-level pricing field with the same shape as GET /v1/vehicles/{id} pricing: listing_price, sold_price, discount_config, breakdown (including estimated_landed), auction_fee_config, history, and market_assessment. The snapshot is captured at order-creation time — it reflects the FX rate and cost config at the moment POST /v1/orders was called and does not change afterward.
- Null for legacy orders. Orders created before this release, and any order where the FX provider was unavailable at creation, return
pricing: null. - Auction orders.
historyis[]andmarket_assessmentisnull(auction sale history is not snapshotted onto orders).auction_fee_configis populated. - Dealer orders.
auction_fee_configisnull;breakdown.dealer_feereflects the dealer fee at creation time. - Webhook parity. Per the v1.18 guarantee,
data.order.pricingin everyorder.*webhook payload is byte-identical toGET /v1/orders/{id}.pricing. No separate webhook update is needed. - Backfill.
POST /v1/orders/backfillnow returns a populatedpricingfor delisted dealer cars — the FX rate is locked at backfill time and landed cost is computed from the stored vehicle snapshot. - Additive. No existing fields were renamed or removed. Clients that ignore unknown fields are unaffected.
v1.41 — 2026-06-19
X-User-Id is now persisted raw on orders (admin-visible) and logged raw alongside the hash.
When X-User-Id is sent with POST /v1/orders, the raw value is now persisted as external_user_id on the order row and is visible on the LMN admin order detail page. It is not returned in the partner-facing /v1 order response — the Order JSON shape is unchanged.
Additionally, every /v1 request that carries X-User-Id now logs both the raw value (external_user_id) and the existing one-way SHA-256 hash (external_user_id_hash) in the audit log. Previously only the hash was captured.
This is a reversal of the prior hash-only stance (#121). No partner-visible response changes — the Order resource and all existing fields are unchanged. See Authentication → X-User-Id for the updated table.
v1.40 — 2026-06-17
Breaking (dealer source):dealer_inspection.insurance_amount is now whole USD, not KRW.
GET /v1/vehicles/encar_<id> now returns dealer_inspection.insurance_amount as a whole-USD integer, FX-converted server-side — matching the auction accident_cost_summary.insurance_paid convention. Previously it was raw KRW.
- Action required if you render this field: stop applying
fx_rateclient-side and stop labelling it₩— the value is already USD. A car that previously reportedinsurance_amount: 1850000(₩) now reportsinsurance_amount: 1370($). - Unchanged:
insurance_history(own-damage claim count) is still an integer count;nullstill means “no data reported” and0still means “no claims”. - Auction source unaffected — its
accident_cost_summarymonetary fields were already whole USD.
v1.39 — 2026-06-15
Purchased vehicle snapshots for dealer orders.- Added
GET /v1/orders/{id}/vehicle, returning the storedVehicleDetailsnapshot captured when the order was created. - Dealer (
encar_*) order creation now stores a full vehicle-detail snapshot so partners can still view condition, inspection, and photos after Encar removes the live listing page. - Dealer photos and image URLs inside inspection data are mirrored to LMN-owned
https://storage.googleapis.com/lmnauto-auction-data/...objects before the snapshot is persisted. - Added
404 order_vehicle_snapshot_not_foundfor historical orders created before purchased-vehicle snapshots were available.
v1.38 — 2026-06-12
X-User-Id end-user attribution header documented.
You can attach an optional X-User-Id header to any request to attribute it to the individual end-user/dealer in your org (per-dealer analytics/curation). No validation, no auth semantics — opaque metadata. LMN’s request logs store a one-way SHA-256 hash of the value, never the raw string. See Authentication → X-User-Id.
Capture has been live since 2026-06-09; this entry documents the contract. The header is not persisted on order resources.
v1.37 — 2026-06-11
Eagle Eye: four new filter fields, per-field flexibility, and match-type metadata on every response surface.New filter fields (filters block, all Eagle Eye request bodies)
fuel(string) — single lowercase fuel token. Accepted values:gasoline,diesel,lpg,hybrid,gasoline_hybrid,diesel_hybrid,electric,plug_in_hybrid,hydrogen. Multi-fuel is not supported — the dealer (encar) search DSL cannot express a fuel OR clause; supply exactly one token or omit. An array or unrecognized token returns400 validation_error.no_accident(boolean) —truefilters to strictly clean cars. Semantics differ by source: dealer applies encar’s strict 무사고 predicate (zero frame damage and zero exterior repair records); auction applies grade A (no structural exchange; exterior panel work is permitted). Not source-equivalent — seeno_frame_damagefor the portable alternative.no_frame_damage(boolean) —trueexcludes structural frame damage while permitting exterior repair. Source-equivalent: dealer maps toAccident.N or Accident.F(zero frame exchange); auction maps to grades A + B (no structural exchange). Recommended for frame-sensitive buyers across mixed sources.estimated_landed_max_usd(number) — maximum estimated landed cost in USD, computed per-partner using your FX rate and freight configuration. Vehicles whose landed cost cannot be computed are excluded. FX drift can move vehicles in and out of an active watch — set a margin or useflexibility.fields.estimated_landed_max_usd.
New top-level flexibility block
An optional block that widens filter bounds and expands categorical alternatives without changing the dealer’s stated criteria. When absent, all filters are applied strictly.
Numeric / year fields accept a level tolerance (none | small | medium | large):
| Level | Max bound widening | Year slack |
|---|---|---|
none | 0% | ±0 |
small | +2% (floor) | ±0 |
medium | +5% (floor) | ±1 year |
large | +10% (floor) | ±2 years |
model, trim, fuel) accept allowed_values — each value adds one parallel search variant. level: "none" is an explicit no-op for per-field override of flexibility.default.
Non-flexable fields (no_accident, no_frame_damage, source, make) accept level: "none" only.
Caps: max 4 allowed_values per categorical field; max 8 total variant product across all categorical fields. Violations return 400 eagle_eye_invalid_flexibility.
A PUT /watches/{id} without a flexibility key clears it to null (full-replace semantics).
New match metadata on every response surface
match_type ("exact" | "flex") and flex_detail are now present on:
- Search rows from
POST /v1/eagle-eye/search - Match list items from
GET /v1/eagle-eye/watches/{id}/matches additionsandprice_changesentries ineagle_eye.matchwebhooks
flex_detail is a keyed object — one entry per field that required the tolerance:
- Numeric entry:
{ requested, level, effective, actual }(all numbers) - Categorical entry:
{ requested, allowed_values, actual }(all strings)
match_reason (filters / signals.*) is unchanged — it remains orthogonal to flex classification.
New error code
eagle_eye_invalid_flexibility(400) —flexibilityblock is structurally invalid: field key references an absent filter, non-nonelevel on a non-flexable field,allowed_valueson a non-categorical field, categorical non-nonelevel withoutallowed_values, or variant cap exceeded.details.fieldnames the offending field.
v1.36 — 2026-06-11
Dealer (encar_*) WAIT advertisements are now rejected as unavailable, like SOLD.
v1.35 made detail and order creation reject dealer cars whose upstream advertisement status is SOLD. The same treatment now applies to status WAIT: GET /v1/vehicles/encar_<id> and POST /v1/orders return 404 vehicle_not_found.
A 100-car sample (2026-06-11) confirmed every WAIT listing is dead inventory: Encar renders its own “sold or deleted” page and its live vehicle API returns 404 for them — only the stale embedded detail-page state still carries vehicle data, which previously let these cars appear buyable.
- No response-shape change. Active (
ADVERTISE) cars are unaffected. - Operational meaning unchanged:
404 vehicle_not_foundon anencar_*detail/order still means “not available to buy — don’t retry.”
v1.35 — 2026-06-10
Dealer (encar_*) detail and order creation now reject sold upstream advertisements as unavailable.
Partner-visible behavior change (dealer source only)
GET /v1/vehicles/encar_<id> and POST /v1/orders with a dealer vehicle_id now return 404 vehicle_not_found when Encar’s detail page is still reachable and parseable but the embedded advertisement status is SOLD. Previously detail could return a successful VehicleDetail, and order creation could create an order, for a car that was no longer active dealer inventory.
- Affected endpoints:
GET /v1/vehicles/{id}for dealer IDs andPOST /v1/orderswhenvehicle_idisencar_<numeric>. - Successful response shapes unchanged: active dealer cars still return the same
VehicleDetail/Orderobjects; no fields were added or removed. - List/search unchanged:
GET /v1/vehiclesresponse shape and filters are unchanged. - Operational meaning: treat
404 vehicle_not_foundon anencar_*detail/order as “not available to buy” — either the upstream returned 404 or the upstream detail page marked the advertisement as sold. Retry/backoff is still appropriate for503 dealer_upstream_unavailable, not for this 404.
v1.34 — 2026-06-09
options_include now filters dealer (encar_*) rows server-side.
Previously, options_include was silently dropped for source=dealer — the filter was neither applied nor surfaced, so source=dealer&options_include=sunroof returned every matching dealer car, not just sunroof-equipped ones. It is now applied for dealer rows via Encar’s native Options. search DSL, with the same AND semantics as auction (a car must have all listed options). A mixed source=glovis,dealer request now honors options_include on both halves.
Two tokens are unsupported for dealer and now return 400 validation_error (details.field: "options_include", with the offending token named): panoramic_sunroof and dashcam. Encar publishes no search filter for these, so rather than silently drop them, the request fails fast. This 400 is dealer-scoped — auction-only requests are unaffected, and a mixed request carrying one of these tokens returns 400 (not a degraded auction-only 200). All other documented options_include tokens work for dealer.
No response-shape change. If you were relying on the previous silent no-op (sending options_include to dealer and ignoring it), your dealer result set will now be narrower; drop the param if you don’t want option filtering.
v1.33 — 2026-06-05
Dealer (encar_*) inspection_report.inspector_notes is now populated (English).
The Encar inspector’s free-text opinion (특기사항 및 점검자의 의견) was previously dropped to null on dealer responses: it’s raw Korean, and the partner-response Hangul scrub removes untranslatable Korean. It is now translated to English via Google Translate on the dealer fetch path — the same English-everywhere experience auction sources already provide — so the note (e.g. “Partial putty/paint and corrosion; replaceable frame parts not disclosed. Over 200,000 km — self-warranty. Driver-side inside-panel damage.”) reaches the partner.
No shape change — inspector_notes is still string | null. It remains null only when the source genuinely has no opinion note or the inspection couldn’t be fetched. Translation is best-effort: on a transient translate failure the note is omitted rather than shown in Korean.
v1.35 — 2026-06-17
New optional order statusinspection_ready — the inspected path is now placed → inspection_in_progress → inspection_ready → acquiring.
Non-breaking additive (new enum value on Order.status)
inspection_readymeans the optional pre-acquisition inspection has completed and LMN can either continue to acquisition (inspection_ready → acquiring) or cancel for inspection failure (inspection_ready → cancelled,cancellation_reason: "inspection_failed").inspection_in_progressno longer exits directly toacquiringorcancelledin the finalized inspection flow. Admins mark the inspection complete first, then take the acquisition/cancellation decision frominspection_ready.- LMN-owned — partners cannot set
inspection_ready. Attempting it viaPOST /v1/orders/{id}/statusreturns403 invalid_status_transitionwithdetails.reason: "partner_not_authorized", alongside other LMN-owned statuses. order.status_changedwebhooks now fire forinspection_in_progress → inspection_readyand for exits frominspection_ready(inspection_ready → acquiring,inspection_ready → cancelled).
status enum MUST tolerate the new value (handle unknown enum values defensively, per conventions).
v1.32 — 2026-06-05
inspection_report.accident_cost_summary now carries a per-incident incidents[] breakdown.
Added (additive — no fields removed, no shape change to existing fields)
inspection_report.accident_cost_summary.incidents[]— one entry per insurance-settled accident (보험사고이력 : 내차 피해):{ date: string | null, insurance_paid, repair_cost: number | null }. All monetary values are whole USD, already FX-converted server-side. Currently populated for Glovis; other sources return[](no per-incident detail upstream).
insurance_paid equals the sum of incidents[].insurance_paid — rounding to whole USD is applied per incident and then summed, so a per-incident list always reconciles exactly to the total. This lets partners render “N accidents → payout per accident → total” without the figures disagreeing. Existing consumers that only read the aggregate fields are unaffected.
v1.31 — 2026-06-03
New optional order statusinspection_in_progress — the order lifecycle is now 10 statuses (was 9), with inspection_in_progress positioned between placed and acquiring.
Non-breaking additive (new enum value on Order.status)
inspection_in_progressis an optional pre-acquisition state. Inspection is not mandatory:placed → acquiringdirectly remains valid for orders that skip inspection;placed → inspection_in_progress → acquiringis the inspected path. Soplacednow has two forward paths.- LMN-owned — partners cannot set it. Attempting it via
POST /v1/orders/{id}/statusreturns403 invalid_status_transitionwithdetails.reason: "partner_not_authorized", alongsideacquiring,secured,export_processing,in_transit. - Inspection failure resolves to
cancelled(reasoninspection_failed), neverfailed.inspection_in_progress → failedis not a valid transition. order.status_changedwebhooks now fire on entry to and exit frominspection_in_progress(placed → inspection_in_progress,inspection_in_progress → acquiring,inspection_in_progress → cancelled) and may carryinspection_in_progressinstatus/previous_status.
status enum MUST tolerate the new value (handle unknown enum values defensively, per conventions).
v1.30 — 2026-06-03
Dealer (encar_*) vehicles now populate vin, transmission, engine_cc, and trim.
Partner-visible response change (dealer source)
These four fields were previously alwaysnull for dealer (encar_*) listings. They are now resolved from the encar listing and its inspection report:
transmission(auto | manual | cvt | dct) andengine_cc— from the listing spec.trim— now uses encar’s canonical English grade name (a Korean grade that previously failed translation was being dropped tonull).vin— from the listing when present, otherwise promoted from the inspection report’s 차대번호 (Section 1).
vin is still typed string | null. It is null only when encar publishes no VIN for the listing — a minority of cars (e.g. listings whose inspection is an abbreviated record without the structured form). Across a random dealer sample, ~98% of cars now carry a VIN. Treat vin: null as “not available from source” and request the VIN from the dealer where your flow requires it (e.g. funding) — do not infer it.
No fields were added or removed and the contract shape is unchanged — these fields simply carry data for dealer rows now. Auction (glovis/sk/aj/lotte/kcar) behaviour is unchanged.
v1.29 — 2026-05-28
pricing.breakdown.ocean_freight now varies by vehicle — it is no longer a flat $1,300 constant.
⚠️ Partner-visible response change (both auction and dealer sources)
ocean_freight (in pricing.breakdown, on both VehicleSummary and VehicleDetail) is now resolved per vehicle from its body type and maker:
- Mercedes-Benz —
1800for any body type. - Other makers, SUV-class (SUV, minivan, van, truck) —
1800. - Other makers, sedan-class (sedan, compact, wagon, coupe, convertible) —
1500. - Unknown / missing body type (non-Benz) —
1500. Dealer (encar_*) cars carry no body type, so they resolve via the Benz rule or this default.
1300 is no longer returned by any vehicle. estimated_landed reflects the per-vehicle freight accordingly. Read pricing.breakdown.ocean_freight from each response rather than hardcoding a value — the rates are operator-tunable and may change without a contract version bump. LMN commission (300), and discount mechanics are unchanged.
v1.28 — 2026-05-28
Inspection report reaches full dealer parity. The dealer (encar_*) inspection_report now carries every section our own consumer site renders. No fields were removed — category_grades[] and issues[] are retained for backward compatibility.
Added
inspection_report.inspection_sheet_url(string | null, all sources) — link to the source’s original inspection-sheet PDF. Populated for SK/AJ auction sources;nullelsewhere (including dealer).- Dealer (
encar_*) full parity.inspection_report.dealer_inspectionnow additionally carries:vehicle_info(object) — inspection-form-specific particulars only (Valid Period,First Registered,Warranty,Engine Model,Base Price), keys+values EN-translated. Identity fields (make/model, year, plate, VIN, transmission, fuel) are omitted — read those from the top-level resource.overall_status[]({item, status, detail}) — section-2 rows (emissions, tuning, special history, usage change, recall, odometer, VIN marking, color, major options), EN-translated.photos[]({url, label}) — inspection front/rear photos.certification({inspector, notifier, date}| null) — inspector/notifier raw Korean company names; date translated.registration_no(string | null) — 성능번호 inspection registration number.
inspection_report.inspector_notesnow populated for dealer (previouslynull) — raw Korean free-text passthrough.dealer_inspection.insurance_history/insurance_amountnow populated from Encar’s insurance-record API (own-damage accident count + cost in KRW) — previously alwaysnull. Adds one upstream call to the dealer detail path; failures degrade tonulland never block the response.
v1.27 — 2026-05-28
Clean dealer (encar_*) cars now return a populated inspection_report (was null), and dealer checklist[] is now populated. A dealer car with no panel damage previously returned inspection_report: null — partner UIs showed “No inspection report available” even though the inspection existed and was simply clean. Root cause: the server discarded any inspection that had no damaged panels and no frame/panel status row, conflating “no damage” with “no data.”
Partner-visible response change (dealer source only)
- Clean cars now return a report.
GET /v1/vehicles/encar_*returns a populatedinspection_reportwhenever Encar’s inspection page is reachable. A clean car reads as zero damage (dealer_inspection.critical_frame_damage = 0,exterior_damage = 0,has_accident = false,accident_summary = "No structural damage") with a populated mechanicalchecklist[].inspection_report/body_conditionarenullonly when Encar genuinely has no record (upstream 404) or a transient fetch failure occurs. inspection_report.checklist[]is now populated for dealer cars — up to ~35 entries{ group, item, result }covering the mechanical systems (engine, transmission, drivetrain, steering, braking, electrical, fuel), KO→EN translated with the same government-form dictionary used for auction sources and the consumer site. Every form row is passed through; an item the inspector left unmarked has an empty-stringresult.- New field
inspection_report.dealer_inspection.simple_repair(boolean | null) — Encar’s 단순수리 (simple repair) flag.nullwhen the page doesn’t state it. Additive, non-breaking. has_accidentnow also reflects the accident-history block, not just frame-damage counts — a car with a cleanly replaced/repaired part (no panel-diagram marker) but recorded accident history now correctly reportshas_accident: true/accident_summary: "Accident history reported".- Auction sources too: a clean car now returns a non-null
body_condition. Previously an auction car whose inspection found no damage and had no pre-rendered diagram image returnedbody_condition: null; it now returns{ image_url, panels: [] }.nullis reserved for cars with no inspection data at all. (Glovis was unaffected — it always carries animage_url.)
v1.26 — 2026-05-27
fuel multi-word values now snake_case. Multi-word fuel tokens previously leaked an upstream space-form ("gasoline hybrid", "plug-in hybrid") into the partner contract — partner-visible in VehicleSummary.fuel, in /v1/vehicles/facets fuels[], and in any URL that needed encoding. Aligned to the options_include snake_case convention.
⚠️ Partner-visible response change (both auction and dealer sources)
VehicleSummary.fuel now emits snake_case for every multi-word value across all sources — auction (glovis_*, aj_*, kcar_*, etc.) AND dealer (encar_*). Previously the auction-side path bare-lowercased the BQ-stored string ("gasoline hybrid" survived), while the dealer-side path went through a Korean→English reverse map that emitted TitleCase-then-lowercase (also "gasoline hybrid"). v1.26 routes both through the same toPartnerFuelToken canonicalizer, so both emit "gasoline_hybrid" going forward.
- Affected values:
gasoline_hybrid(was"gasoline hybrid"),diesel_hybrid(was"diesel hybrid"),plug_in_hybrid(was"plug-in hybrid"), plus future multi-word values surfaced by facets (gasoline_plug_in_hybrid,lpg_plug_in_hybrid,dual_fuel_lpg— emission only; filter for these still no-ops until follow-up PRs land their FUEL_MAP entries). - Single-word values unchanged:
gasoline,diesel,hybrid,electric,lpg,hydrogen. - Why no deprecation period: the space-form was never documented in
schemas.mdx. Partners writing strict-enum validators against the published contract weren’t accepting the space-form anyway; partners reading values from facets or accepting whatever the response carried receive the new form transparently. - Unknown-fuel fallthrough also now canonicalizes. If encar emits a brand-new fuel token before we update FUEL_MAP, the partner receives the snake_cased form (e.g.
"new fuel type"→"new_fuel_type") instead of the bare lowercased raw — keeps the format invariant intact even for forward-compat surface.
Other surfaces
- Facets.
/v1/vehicles/facetsfuels[]now emits the same snake_case canonical form. Partners that read facets to build filter UIs receive values that work as?fuel=filter inputs without further transformation. - Filter input is lenient.
?fuel=accepts both the new snake_case form AND the legacy space-form (URL-encodedgasoline%20hybridstill resolves correctly), as well as TitleCase. Mintlify documents only the snake_case form going forward; the lenient input is internal robustness, not a documented alias. Orderresource inherits the fix automatically —Order.fuelis computed at read time from the vehicle resource, so any hydrogen Nexo order placed during or after v1.26 reflects the new format on both the synchronous response and the webhookdata.orderpayload (v1.18 invariant preserved).
v1.25 — 2026-05-27
Hydrogen added to thefuel enum. gasoline | diesel | hybrid | electric | lpg | hydrogen — partners can now filter for hydrogen vehicles (Hyundai Nexo and similar FCEVs) via ?fuel=hydrogen, and the response fuel field returns "hydrogen" instead of leaking the raw upstream Korean "수소".
- What broke before.
?fuel=hydrogenwas silently dropped (no matching forward map entry), so partners filtering for hydrogen received unfiltered mixed-fuel results. Separately, dealer responses with a Korean source token would emitfuel: "수소"to the partner — partner UIs with strict enum validators rejected the row. - What’s fixed. Both directions covered by a single FUEL_MAP entry: lowercase
hydrogentranslates to encar’s수소upstream, and수소translates back tohydrogenin responses. - Demand. Discovered via partner inventory probes on 2026-05-27 — facets endpoint was already advertising
hydrogenas a valid value because two Hyundai Nexos sat in dealer inventory; partners reading facets would reasonably expect the filter to work.
v1.24 — 2026-05-27
Dealer (encar_*) inspection data now populated on GET /v1/vehicles/encar_*. The existing inspection_report and body_condition fields are no longer mostly-empty on dealer cars — they now carry the same per-panel damage data that encar’s own consumer site renders.
- Partner contract unchanged. Same shape, same field names. Partners reading
inspection_report.dealer_inspection.{critical_frame_damage,exterior_damage,frame_status,panel_status}andbody_condition.panels[]start seeing populated data on cars where it was previously null. No client changes required. - New data source. Internally we switched from encar’s JSON inspection endpoint (which was empty for most dealer listings) to encar’s HTML inspection page (which encar’s own UI uses). Same parser as our consumer apps — single source of truth, no risk of drift between partner API and
lmnauto.com. - Coverage. Cars with a posted inspection report now return populated data. Cars with no posted inspection (a small minority, typically dealer-direct listings) still return
inspection_report: nullandbody_condition: null— same as today. - Insurance fields (
insurance_history,insurance_amount) staynullfor now. The HTML page does not expose them in structured form; we’ll surface them in a future release if we wire up encar’s separate carHistory API.
v1.21 — 2026-05-26
Order.options[] added — vehicle feature names are now snapshotted onto the order. The Order resource now includes an options array of English Title-case feature names (e.g. Sunroof, Leather Seats, LED Headlamps) — byte-equal to VehicleDetail.options on GET /v1/vehicles/{id} for the same vehicle. Partners no longer need a follow-up vehicle lookup to read feature information after creating an order.
- Captured at order creation. The list is snapshotted from the live vehicle at
POST /v1/orderstime (BigQuery for auction sources, encar detail parse forencar_*dealer sources) and stored on the order row, mirroring the existingvin/license_platesnapshot pattern. - Webhook parity. Per the v1.18 guarantee, the same field appears on
data.orderin everyorder.*webhook event — no separate update needed. - Format. English Title-case strings for both auction and dealer rows. Dealer (
encar_*) values are translated through the same dictionary that powers the consumer site, so what you receive on the API matches what’s rendered atlmnauto.com/.../encar_*. (Theoptions_includequery parameter onGET /v1/vehiclesstill uses snake_case tokens — that’s a separate filter input, not a response format.) - Backfill behavior. Orders created before v1.21 return
options: []. We do not retroactively populate; the snapshot is creation-time. - No breaking changes. Purely additive. Consumers that ignore unknown fields continue to work; consumers that want to read
optionscan do so immediately on new orders.
v1.21 hotfix (same day, included in same release)
Between the initial v1.21 deploy and partner announcement we caught that dealer (encar_*) VehicleDetail.options and the resulting Order.options snapshot were emitting raw Korean strings (e.g. 선루프) instead of English (Sunroof) — inconsistent with the consumer site and the auction-source path, which both translate. Fix:
GET /v1/vehicles/encar_*and the order-creation snapshot now both apply theENCAR_OPTION_KO_ENdictionary used by the consumer site. New dealer orders will carry English names.- One-shot DB migration (
0065_backfill_dealer_order_options_en.sql) translates the handful of dealer-order rows that snapshotted Korean strings during the brief window before the fix.
v1.20 — 2026-05-26
Landed-cost preview on list view.GET /v1/vehicles now returns pricing.discount_config and pricing.breakdown on every row of VehicleSummary. Same shape, same values as the detail endpoint — your list-view cards can render estimated landed cost (and the underlying line items) without a follow-up GET /v1/vehicles/{id} per row.
- Strictly additive. No fields renamed or removed. Existing list-view consumers see new fields appear; ignoring them is safe.
- Parity guaranteed. For a given vehicle,
summary.pricing.discount_configandsummary.pricing.breakdownare byte-equal to the same fields undervehicle.pricingfrom the detail endpoint. auction_fee_configremains detail-only. The fee schedule is constant per source/category, so you can hold a static copy locally rather than receive it on every list row.- Pilot constants still apply. LMN commission 1,300, dealer fee/discount $300 each — same as v1.16. Future versions will externalize these per partner/market/source.
v1.19 — 2026-05-26
Dealer (encar_*) listings are now orderable. POST /v1/orders accepts vehicle_id values with the encar_ prefix, completing the dealer source loop that started with v1.15 (read) and v1.16 (pricing).
- Buy-now semantics. Dealer listings are fixed-price —
max_bid_amount_usdMUST be omitted ornull. Non-null dealer values return400 validation_error. - No auction cutoff. Dealer orders have
auction_date: nullandorder_cutoff_at: null. Thepast_order_cutofferror does not apply. - No competitive bidding. Responses always carry
is_highest_bid: nullandcurrent_max_bid_usd: null(dealer orders are not bid against other partners). - New error code:
dealer_upstream_unavailable(503) can now surface onPOST /v1/orders— fired when the encar detail upstream times out, returns 5xx, or returns an unexpected response shape during order creation. Retry-safe; the order is not created. Upstream 404 still maps to404 vehicle_not_found(listing removed). - Idempotency, duplicate-order, and FX-lock semantics are unchanged — the same
Idempotency-Keyrules apply,(api_key, vehicle_id)active-order uniqueness is enforced, andfx_rateis locked at creation. Settlement-side semantics (no auction-fee config, dealer fee already inpricing.breakdown.dealer_fee) follow the v1.16 dealer pricing shape.
v1.18 — 2026-05-20
Webhook payload ⇔ GET response parity. Thedata.order field of every order.* webhook event is now structurally identical to the response of GET /v1/orders/{id}. Same fields, same types, same enrichment.
is_highest_bidandcurrent_max_bid_usdnow populated in webhook payloads (previously alwaysnull). They are computed at emit time against the active bid landscape, matching the GET endpoint exactly.- Implementation guarantee: both code paths route through the same response mapper, and an invariant test enforces structural equality on every PR. Partners can reuse a single deserializer across both channels.
- No breaking changes: if your handler was tolerating
nullfor these two fields, it’ll continue to work. Webhook consumers who also callGET /v1/orders/{id}can drop the redundant call.
v1.17 — 2026-05-13
Order state-machine refactor: 9-state linear lifecycle. Replaces the 6-state model +fulfillment_detail field with explicit per-phase statuses. Partner integration must update.
proceedrenamed toplacedeverywhere — order resource, filter enum, error messages, sandbox lifecycle.shippingstatus removed. Logistics phase is now expressed as three explicit statuses:export_processing— Korean export workflow underway (LMN-set on entry fromsecured).in_transit— Bill of Lading issued and vessel has departed Korean port (LMN-set, not partner-set as previously planned).customs— vessel arrived at destination port; vehicle in customs-bonded area (partner-set viaPOST /v1/orders/{id}/status).
fulfillment_detailfield removed from the Order resource and fromPOST /v1/orders/{id}/statusrequest bodies. Sending it returns400 validation_error. Theawaiting_pickupenum value is dropped — folded intoexport_processing.order.fulfillment_updatedwebhook event removed. All logistics-phase changes now fireorder.status_changedinstead. Update any switch-on-event-type handlers.- Partner-driven status pushes simplified to two transitions:
in_transit → customs(vehicle arrived destination port).customs → delivered(terminal, mandatory).
- State machine invariants tightened:
failedis reachable fromacquiringonly. Post-securedfailures are out of scope; they go offline.cancelledis reachable fromplacedonly. Post-acquiringcancellation goes offline.
- Reason routing for
auction_cancelledandseller_withdrew: terminal is decided by the order’s current status at the moment of the upstream event. Pre-acquiringdiscovery routes tocancelled(viacancellation_reason); post-acquiringroutes tofailed(viafailure_reason). Same reason string, different field — webhook consumers should treat the terminal as authoritative. - Competing-order cohort rule: when multiple partners hold
placedorders on the same auction lot, all transition toacquiringtogether when LMN starts bidding. No order goesplaced → faileddirectly. - Zombie cleanup: orders stuck in
placedpastauction_date + 24hare auto-resolved tofailedwithfailure_reason: auction_passed. The webhook collapses the synthetic intermediate state —previous_status: "placed"with an additivesynthetic_acquiring: truediscriminator. Treat as a normalfailedevent, or branch on the discriminator to suppress noise. - Reschedule auto-cancel scope:
placed → cancelled(auction_rescheduled_too_soon) only fires fromplaced. Orders already inacquiringproceed normally on the new auction date (a separateorder.auction_rescheduledevent still fires). - Error taxonomy clarified:
invalid_status_transitionnow distinguishes via HTTP status —403= partner attempted an LMN-owned status (details.reason: "partner_not_authorized"),409= sequence violation (details.reason: "out_of_sequence").details.current_statusset in both cases.
- Replace literal status comparisons:
"proceed"→"placed";"shipping"→"export_processing"/"in_transit"/"customs". - Remove all reads of
order.fulfillment_detail(alwaysundefinednow). - Update
POST /v1/orders/{id}/statusrequest bodies to send{"status": "customs"}or{"status": "delivered"}— neverfulfillment_detail. - Remove
case "order.fulfillment_updated"from webhook handlers; logic moves into theorder.status_changedhandler. - Add support for the new
synthetic_acquiring: trueevent-payload variant (or ignore it — partners can treat it as a normalfailed).
v1.16 — 2026-05-13
Pricing shape rework: symmetric fees + fixed discount. Tightens dealer pricing semantics and makes the fee model uniform across sources.pricing.breakdown.auction_feeanddealer_feeare now always integers. Branch onsource, not onnull.- Auction rows:
auction_feepopulated as before;dealer_fee: 0(wasnull). - Dealer rows:
auction_fee: 0(wasnull);dealer_fee: 300flat USD (was the KRW-derived 440,000 KRW conversion).
- Auction rows:
pricing.discount_configis now always populated. Wasnullfor dealer.- Auction: bid-based fields unchanged.
- Dealer:
bid_threshold,rate_below,rate_at_or_aboveare all0(bid-based logic does not apply).
- New field:
pricing.discount_config.fixed_discount_amount(whole USD). Flat discount subtracted fromestimated_landedat quote time, independent of bid.- Auction:
0today. - Dealer:
300— exactly offsets the dealer fee. Net economic effect onestimated_landedis zero, but the two levers are now independent and can be adjusted separately.
- Auction:
estimated_landedformula updated to subtractfixed_discount_amountin both auction and dealer paths. Auction landed values are unchanged today (fixed_discount = 0); dealer landed is unchanged today (+300 − 300cancels).- Migration note for the recompute formula:
your_total_discount = your_bid_rate_discount + fixed_discount_amount(the bid-rate component is unchanged from v1.15).
v1.15 — 2026-05-13
New feature.source=dealer is now functional — backed by a live proxy to a third-party retail inventory (encar). Roughly 10–18× expansion in accessible inventory for common Korean used-car queries compared to auction-only.
source=dealeris queryable.GET /v1/vehicles?source=dealer&make=<make>&...returns dealer listings withidprefixencar_<numeric>,source: "dealer", and a dealer-shapedpricing.breakdowncarrying the dealer fee. (Pricing shape further refined in v1.16 — see above.)- Dealer constraints (new 400s):
source=dealer(alone or mixed) requiresmake. Multi-valuemakerejected.- Combining
source=dealerwith any auction-only filter (auction_status,accident_grade,exterior_grade,auction_date_*,auction_count_*) returns400 validation_error. - Mixed
source=glovis,dealeris first-page only — passingcursorreturns400 mixed_source_pagination_unsupported. Paginate each source separately.
- Filter parity gaps for dealer:
transmission, multi-valuefuel,keyword,body_styleare silently dropped from the dealer half (upstream DSL doesn’t support them). Auction half of a mixed request still honors them. (options_includewas also dropped at launch but now filters dealer rows server-side — see v1.34.) - New response headers:
X-LMN-Dealer-Page-Max: 48— whenlimit > 48and dealer is in scope (upstream caps page size). Paginate dealer separately viacursor.X-LMN-Partial-Dealer-Unavailable: <csv>— when at least one dealer source failed. The value is a comma-separated list of the failed dealer source names (encar,danawa, orencar,danawa). On a mixed request the 200 is still valid and auction is authoritative; on a pure dealer request a single source down still returns the survivor’s rows + this header (only an all-sources-down dealer request returns 503).
- New error codes:
400 mixed_source_pagination_unsupported— see above.503 dealer_upstream_unavailable— pure dealer request failed because the upstream is unreachable or returned an unexpected response shape. Mixed requests degrade instead of returning 503.
- Detail endpoint accepts dealer IDs.
GET /v1/vehicles/encar_<numeric>returns dealer detail with auction-only fields (comparables,body_condition,inspection_report,pricing.history,pricing.market_assessment,auction_fee_config) allnullor empty.404 vehicle_not_foundonly on upstream HTTP 404; other failure modes → 503. (discount_configshape further refined in v1.16 — now populated for dealer with zeroed bid-based fields.) - Mixed-source sort semantics: When
sort=auction_date_asc(the default) is used on a mixed query, thelimitbudget is split between auction (sorted by date) and dealer (sorted byyeardesc, since dealer rows have no auction date). Other sort options sort across the merged stream as before. /v1/vehicles/facetssourcesarray now includes"dealer"when the upstream is reachable. Health is probed lazily and cached for 60 seconds; partners can treat the facets response as the canonical “currently queryable sources” signal.
v1.14 — 2026-05-13
Breaking change inpricing.breakdown. korean_export removed.
korean_exportremoved frompricing.breakdown. Korean-side export logistics are folded intoocean_freight. The field was previously deprecated (held at0for auction,nullfor dealer) but no production partner ever consumed the new shape, so the removal lands pre-launch with no migration window.- Clients that read
pricing.breakdown.korean_exportwill getundefined; remove those reads and rely onocean_freightfor the combined Korean-export + ocean-freight figure.
v1.13 — 2026-05-12
Breaking change inpricing. Regenerate clients before consuming GET /v1/vehicles/{id}.
korean_exportdeprecated inpricing.breakdown(superseded by v1.14 removal — see above). Held at0for auction vehicles,nullfor dealer vehicles. Korean-side export logistics are now folded intoocean_freight.lmn_commissionvalue changed:$1,500→$300.ocean_freightvalue changed:$2,500→$1,300(now covers Korean export + ocean freight combined).pricing.discount_configadded (sibling toauction_fee_config). Shape:{ bid_threshold, rate_below, rate_at_or_above }. The discount is bid-dependent —bid_thresholdis compared against the partner’s bid amount in USD, not the listing price. Partners applyrate_below/rate_at_or_aboveto their own bid to compute the discount for that bid; the invoice realizes the same rule against the actual hammer price (purchase_price_usd), which may be lower thanmax_bid_amount_usd. Current values:{ bid_threshold: 25000, rate_below: 1.5, rate_at_or_above: 2 }.pricing.breakdown.estimated_landedsemantics changed to an illustrative single-bid total. Both bid-dependent fees (auction_feeand the discount derived fromdiscount_config) are now evaluated against the same internal basis price LMN selects:sold_priceif available, otherwise LMN’s market-derived final-price estimate, otherwise the auction starting price. For exact totals at YOUR bid, recompute usingauction_fee_configanddiscount_config.estimated_landedis no longer a quote; it is an estimate.- See the Vehicles endpoint guide section “Recomputing landed cost” for the exact bid-level formula, rounding rules, and settlement caveats.
v1.12 — 2026-05-11
Classification: non-breaking infrastructure change. No request/response shape changes.fx_rate contract is unchanged: still number | null, still locked at POST /v1/orders, still immutable across PATCH / status updates, still null only when the upstream FX provider was unavailable at creation time.
- FX source unified.
fx_rateand all derived currency conversions now resolve through a single source — openexchangerates.org/historical/{date}.json, wheredateis yesterday in KST (D-1 close lock, KST midnight rollover). Previous behavior pulled USD/EUR/JPY/etc. from Frankfurter (ECB) and overlaid OXR for select managed-float currencies; consolidation removes that split. fx_rate: nullsemantics unchanged. When openexchangerates.org is unreachable atPOST /v1/orders, the order still creates withfx_rate: nulland LMN applies the settlement-day rate offline atsecured, exactly as before.- No action required for partners. No fields added or removed. No code regeneration needed.
v1.11 — 2026-05-06
vin added as a photo_tags[].tag value. When the API can identify the photo that shows the VIN plate (차대번호), that photo is tagged 'vin' in photo_tags[]. At most one vin tag per car. The position of the vin-tagged photo in photo_tags[] is not positional; it can appear at any index. Other tags remain positional heuristics from photos[] array order. The 'vin' tag may be absent — partners should not assume every car has it. Backwards-compatible: clients that ignored unknown tag values continue to work; clients that enum-validated tags must regenerate from the OpenAPI spec.
v1.10 — 2026-05-01
Vehicle response contract update for the initial partner pilot. Regenerate clients before consumingGET /v1/vehicles or GET /v1/vehicles/{id}.
- Vehicle pricing stays in
pricing, with USD as the documented unit. Every monetary number in partner vehicle responses is a whole USD integer unless explicitly documented otherwise. - Nested vehicle pricing fields no longer use
_usdsuffixes. Renames include:pricing.listing_usd→pricing.listing_price- top-level
sold_price_usd→pricing.sold_price pricing.breakdown_usd→pricing.breakdownpricing.history[].price_usd→pricing.history[].pricepricing.similar_sales[]→comparables.past_sales[]pricing.similar_upcoming[]→comparables.upcoming_sales[]pricing.market_assessment.market_median_usd→pricing.market_assessment.market_median
pricing.delta_pctwas removed. Usepricing.history,comparables.past_sales, andpricing.market_assessmentfor price movement and market context.pricing.auction_fee_configis reduced to partner-useful fields only:{ percentage, minimum, maximum }.minimumandmaximumare source/category auction-fee caps converted to whole USD using the response FX snapshot.pricing.breakdown.estimated_landedusessold_pricewhen available, otherwiselisting_price. This makes sold-vehicle landed cost reconcile inside the samepricingobject.pricing.history[]now returns same-car auction appearances. Entries are matched internally by Korean license plate and includevehicle_id,auction_date,price, andresult.vehicle_idis an auction listing/round ID, so it may differ between history rows for the same physical car. This is current-vehicle history, not comparable-car history.- Comparable vehicles moved to
comparables.comparables.past_sales[]is sold-only andcomparables.upcoming_sales[]contains future auction alternatives. Both includevinandlicense_platewhen available. - Vehicle detail now populates
body_condition,inspection_report, andphoto_tags.body_condition.panels[]uses English labels only, andbody_condition.image_urlis returned only for LMN-mirrored media.photo_tags[]items are{ url, tag }; array order followsphotos. - Sandbox lifecycle testing uses
POST /v1/orders/{id}/sandbox-status. The normalPOST /v1/orders/{id}/statusendpoint remains restricted to partner-owned fulfillment updates anddeliveredin every environment. vehicle.transmissionis now normalized toauto | manual | cvt | dct. Previously the response surfaced raw upstream tokens (A/T,M/T,CVT,DCT); responses now match theschemas.mdxenum. The filter param?transmission=continues to accept both lowercase and raw forms — request shape is unchanged.vehicle.pricing.sold_priceis now strictly null unlessauction_resultissoldornegotiation_sold. Stale upstreamsoldPricevalues onnegotiation_requested/no_bid/upcomingrows are no longer surfaced.vehicle.drivetrainis now an open string in the spec. Unknown drivetrain tokens pass through unchanged; codegen clients should treat the field as opaque rather than enum-rejecting.GET /v1/vehiclesfilter docs:mileage_max_km(upper bound) documented. (Correction: an earlier draft of this entry listedmileage_min_km; it is not supported server-side — onlymileage_max_kmexists.)- Vehicle response examples corrected.
thumbnail_url/photos[]examples now use the actual upstreamlmnauto-auction-dataGCS host that the API returns. Theinspection_reportexample body now reflects the real mapper output (source / overall_grade / grade_description / accident_summary / has_accident / has_frame_damage / category_grades / checklist / accident_history / accident_cost_summary / issues / inspector_notes) instead of the previous placeholder fields.
v1.9.2 — 2026-04-30
Non-breaking (new filter + sandbox-only helper).GET /v1/vehiclesnow supportsauction_status. Useauction_status=upcomingto fetch upcoming listings directly. Allowed values:upcoming,sold,negotiation_sold,no_bid,negotiation_requested.- Sandbox-only
POST /v1/orders/{id}/sandbox-status. Lets pilot partners simulate order status and fulfillment transitions to test webhook delivery. Available only on sandbox hosts with sandbox API keys.
v1.9.1 — 2026-04-29
Classification: non-breaking clarification. No request/response model shape changes. No endpoint path, method, required request field, response object field, or field type changed in this version. Added enum valueOrder.failure_reasonmay now includeseller_withdrew. This is an additive value on the existing nullablefailure_reasonfield. Treat it as a terminalfailedreason.
Order.fulfillment_detailreadable values are now documented as:export_processing,awaiting_pickup,in_transit,customs,in_customs,at_destination_port,delivered.- Partner-writable
fulfillment_detailvalues remain onlyin_transitandcustomsthroughPOST /v1/orders/{id}/status. status: deliveredremains the only partner-writablestatusvalue. Partners still cannot pushstatus: shipping.
- Postman status-push examples no longer send invalid
status: shipping; they now usefulfillment_detail: in_transit. - Postman order-creation examples no longer include stale body field
idempotency_key; idempotency remains theIdempotency-KeyHTTP header.
- Webhook delivery testing now documents cron-window delivery instead of a hard 5-second expectation.
- Pilot webhook destination setup now documents the current
partner_webhook_configsDB row withsecret_ref; there is no partner-facing webhook config route yet.
v1.9 — 2026-04-29
Behavior change (per-partner). Cutoff timing rule changes; partners using fixed cron schedules need to verify against the new value inorder_cutoff_at.
order_cutoff_atis nowauction_date − N minutesper partner. Replaces the prior “auction_date − 1 day, midnight KST” rule. N is configured per integration on the partner record. Default 1440 (24h) for inspection-heavy integrations; integrations without a secondary inspection step use a shorter lead time (e.g. 60 minutes) covering bid submission only. Stop computing cutoff client-side; always readorder_cutoff_atfrom the response.
v1.8.1 — 2026-04-27 (hotfix)
thumbnail_urlnow always returns a working URL. Previously the field surfaced a derivedthumbs/images/...path that 404’d because the upstream thumbnail-generation pipeline isn’t built yet (integration-spec §10 #13). Partner API now returns the rawimages/...URL directly — full-size, but always loads. Run your own resize CDN if list-view bandwidth matters.
v1.8 — 2026-04-27
Non-breaking (additive event types + Order resource fields). Existingorder.status_changed, order.fulfillment_updated, and order.auction_rescheduled handlers are unaffected; existing Order parsers ignore new fields.
vinandlicense_plateon Order resource. Snapshotted at order creation (immutable for the life of the order). Webhook payloads now carry both fields without a follow-upGET /v1/vehicles/{vehicle_id}round-trip — match dealer records directly from the event.vinis null when the upstream listing has no VIN;license_plateis the Korean plate as-shown (e.g.,12가 3456). Added per a partner integration commitment.- New webhook event:
order.price_updated. Fires when the vehicle backing an activeproceedorder has itslisting_usdrefreshed by the upstream scraper. Payload includesprevious_listing_usd,current_listing_usd,delta_usd,delta_pct. Bid-strategy partners can use this to decide whether to PATCHmax_bid_amount_usdmid-flight. Does not fire on terminal-state orders. Seeorder.price_updated. - New webhook event:
order.re_auctioned. Fires when a vehicle from one of your terminal orders (failed/cancelled/delivered) reappears in a new auction round. Matched by Korean license plate so the same physical car is caught even when it moves between auction houses. Useful for offering the dealer a second chance. Deduped via(partner_id, license_plate, new_auction_date)so re-scrapes don’t double-fire. Seeorder.re_auctioned. GET /v1/orders/{id}/eventsdocumented in/endpoints/orders. The endpoint already shipped in earlier pilot builds; the partner-facing docs were missing. Returns LMN-side webhook delivery log (delivery_status,attempts,last_response_code,payload) for self-service “did I miss a webhook?” debugging. See GET /v1/orders//events.- OpenAPI
OrderEventschema corrected. Previously declared fields (event_type,from_status,to_status,actor,metadata) that did not match the live response. Now reflects the actual shape (type,delivery_status,attempts,last_response_code,next_retry_at,created_at,delivered_at,payload). Codegen clients should regenerate.
v1.7 — 2026-04-24
Non-breaking (additive fields + new endpoint). Pilot partners on v1.6 can keep working against the old response shape; the new fields are ignorable.- New endpoint:
PATCH /v1/orders/{id}. Updatemax_bid_amount_usdon an existing order. Allowed only whilestatus=proceedand beforeorder_cutoff_at. NoIdempotency-Keyrequired. See PATCH /v1/orders/ — Update max bid. fx_rateis now locked atPOST /v1/orderstime (previously: atsecured). The KRW-per-USD rate is captured once at order creation, persisted on the row, and does not change via PATCH or status pushes. Gives partners a deterministic maximum settlement exposure from commit time.nullon rare FX-provider outages.- Comparable sales on
GET /v1/vehicles/{id}. Up to 20 comparable cars (same make+model · year ±1 · mileage ±20,000 km · past 4 weeks). Excludes the target vehicle and any same-plate re-listings. See Similar-sales definition. pricing.market_assessmentonGET /v1/vehicles/{id}. One-line server-side price verdict with five labels (great/good/fair/wait/limited) over the sold comparable set, plusprice_dropwhen the same vehicle has appeared in prior auction rounds. See Market assessment.thumbnail_urlnow populated on bothGET /v1/vehiclesandGET /v1/vehicles/{id}(was alwaysnullpre-2.7). Derived from the first auction-bucket image. Returnsnullonly when the listing has no images at all. Past-auction vehicles may surface a URL that 404s until a backfill runs — treat 404 as “no thumbnail”, not a broken API.VehicleSummary.fuelis now nullable. Previously declared non-null in the spec but could be absent in rare upstream data. OpenAPI now marks itnullable: true; partners with strict enum validators must acceptnull. Post fuel-null crash fix deployed 2026-04-23.
v1.6 — 2026-04-18
Breaking for integrators still on v1.5 drafts.- Dealer-deposit removed.
amounts.dealer_deposit_usd,amounts.dealer_refund,amounts.cancellation_refundno longer exist.POST /v1/ordersis a bid commitment, not a deposit attestation; payment is owed only onsecured. - Error codes split into resource-specific.
not_found→vehicle_not_found/order_not_found.unauthorized→missing_api_key/invalid_api_key.forbiddenandinvalid_transitionunified toinvalid_status_transition(with HTTP 403 vs 409 distinguishing partner-attempt vs state-machine). - New
422 idempotency_key_reused. Reusing an Idempotency-Key with a differentvehicle_idnow returns this distinct error rather than silently returning the original order. GET /v1/ordersfilters extended. Addedids(CSV batch lookup, max 100),from/to(half-open [from, to) oncreated_at, replaces the previouscreated_after/created_before),sortenum (created_descdefault,created_asc,auction_date_asc,updated_desc). Cursor is now sort-specific.- Facets response uses plural field names.
fuels,transmissions,sources(wasfuel,transmission,source). Explicit “no per-value counts” — UIs surface plain values. - Timestamp convention made explicit. Source UTC offset must always be preserved — never
Z-normalized. NodeDate.toISOString()flagged as not acceptable.
v1.5 — 2026-04-16
QA + business review fixes: all USD amounts are whole dollars (not cents); idempotency keys are permanent (no TTL); API key length standardized to 32 chars;fulfillment_detail write permissions clarified (LMN: export_processing/awaiting_pickup; partner: in_transit/customs optional); added auction_passed failure_reason for zombie order cleanup; added vehicle_unavailable (410) for buy-now vehicles; clarified cancellation_reason vs failure_reason mutual exclusivity; added FX risk note (auction-time rate); added post-secured disputes note (offline only).
v1.4 — 2026-04-16
Revised competitive bidding: all orders accepted (removedbid_too_low). Response includes is_highest_bid + current_max_bid_usd. outbid_internally happens at auction time, not at order creation.
v1.3 — 2026-04-16
Add competitive bidding:bid_too_low (409) with current_max_bid_usd disclosure. New failure_reason: outbid_internally. Webhook fires when lower bid is replaced.
v1.2 — 2026-04-16
Addauction_fee_usd, failure_reason, shipment fields to Order. Remove fx_snapshot from API. Updated webhook payload examples with realistic data per transition.
v1.1 — 2026-04-16
Addmax_bid_amount_usd to order creation. Add auction_count_min/max vehicle filters. Add price_drop_pct, first_price_usd to vehicle response. Full webhook spec.
v1.0 — 2026-04-16
6-state order model. Collapsed 14-state lifecycle to 6 states:proceed → acquiring → secured/failed → shipping → delivered + cancelled. Removed inspection from order lifecycle (secondary inspection is optional post-purchase service). Removed POST /v1/orders/{id}/decision and GET /v1/orders/{id}/inspection endpoints. Added fulfillment_detail field for granular shipping tracking. Added order_cutoff_at. Renamed eta_lagos → eta_destination with destination_port. Removed inspection_fee from Order amounts. Simplified cancellation to proceed-only via DELETE.