API surfaces
| Surface | Endpoint | What it does |
|---|---|---|
| Search preview | POST /v1/eagle-eye/search | Evaluate a condition once and return current matches. |
| Saved watches | POST /v1/eagle-eye/watches | Save a condition and seed its current match list. |
| Watch management | GET /v1/eagle-eye/watches, GET/PUT/DELETE /v1/eagle-eye/watches/{watch_id} | List, inspect, update, or delete watches. |
| Watch controls | POST /v1/eagle-eye/watches/{watch_id}/mute, /unmute, /pause, /resume | Control notifications and collection. |
| Match inbox | GET /v1/eagle-eye/watches/{watch_id}/matches | Read current matches for a watch. |
| Match triage | POST /v1/eagle-eye/watches/{watch_id}/matches/hide, /matches/unhide | Hide or restore reviewed vehicles in bulk. |
| Event recovery | GET /v1/eagle-eye/watches/{watch_id}/events | Read the webhook history for a watch. |
Schema reuse
Eagle Eye reuses the existing Open API vehicle model. Search results, match rows, and webhookadditions / price_changes embed the same VehicleSummary shape returned by GET /v1/vehicles; Eagle Eye metadata such as watch_id, match_reason, and signal_detail wraps that vehicle object instead of replacing it.
The vehicle_id in a match is the same ID used by GET /v1/vehicles/{id} and the existing order API.
Webhook
Eagle Eye emits one webhook type:additions— new vehicles that matched the watch,price_changes— watched vehicles whose price dropped enough to notify,removals— vehicles that disappeared or no longer qualify.
signal_detail so the partner can explain the alert:
| Signal | Detail |
|---|---|
repeat_listing | listing_count, cumulative_drop_pct, and window_days. |
undervalued | reference_price_krw, listing_price_krw, and delta_pct. |
price_drop | previous_price_krw, current_price_krw, and drop_pct. |
price_drop is drop-only. If a vehicle price increases while still qualifying, the current match updates silently and loses the stale signals.price_drop reason. If the increase makes it fail the watch’s filters, such as rising above price_max_krw, LMN sends a removal instead.
Webhook delivery uses the same HMAC signing and retry behavior as order webhooks.
Acting on a match
Eagle Eye does not create orders directly. A match carries the samevehicle_id used by the existing order API.
When the dealer wants to act, send that vehicle_id to POST /v1/orders.
Filter reference
New filter fields (search and watch bodies)
The following fields are accepted in thefilters block of POST /v1/eagle-eye/search, POST /v1/eagle-eye/watches, and PUT /v1/eagle-eye/watches/{watch_id}:
Single lowercase fuel token. Accepted values:
gasoline, diesel, lpg, hybrid, gasoline_hybrid, diesel_hybrid, electric, plug_in_hybrid, hydrogen.Multi-fuel is not supported. Supply exactly one token — an array or an unrecognized token returns 400 validation_error. The single-token constraint exists because the dealer (encar) search DSL cannot express a fuel OR clause; allowing multiple values would silently produce zero dealer results.true filters to clean cars only. false or absent applies no filter.Per-source semantics differ — see the damage filter semantics section below before using this field across mixed sources. For frame-sensitive buyers, no_frame_damage is the source-equivalent alternative.true excludes cars with structural frame damage; exterior panel repair is permitted. false or absent applies no filter.This filter is source-equivalent — it maps to the same structural-integrity standard on both dealer and auction sources. Recommended for buyers who care about frame integrity but accept repaired exterior panels.Maximum estimated landed cost in USD (positive integer). Computed per-vehicle using your partner’s FX rate and freight configuration.Two implications:
- Null exclusion: vehicles whose landed cost cannot be computed are excluded when this filter is active.
- FX drift: a pure exchange-rate move (no listing price change) can admit or remove vehicles from an active watch. Set your bound with a buffer for normal FX volatility, or use the
flexibility.fields.estimated_landed_max_usdtolerance (see flexibility).
Damage filter semantics
no_accident and no_frame_damage map to different underlying predicates depending on inventory source:
| Filter | Dealer (encar) | Auction (Glovis / SK / AJ / Lotte / KCar) |
|---|---|---|
no_accident: true | Strictly 무사고 — zero frame damage AND zero exterior repair records | Grade A only — no structural panel exchange; exterior panel work is permitted |
no_frame_damage: true | 무사고 or 단순수리 — zero frame exchange; simple exterior repair accepted | Grades A + B — no structural exchange in either grade |
no_accident is not semantically equivalent across sources. On dealer it means zero exterior repairs; on auction it means no structural exchange (exterior work is permitted because grade A is the strongest clean-accident classification auction sources express). no_frame_damage is source-equivalent and is the recommended filter for buyers who need a consistent structural-integrity guarantee across both dealer and auction inventory.
Approximately 3% of dealer stock lacks a valid vehicle inspection record (성능기록부). When either damage filter is active, those cars are excluded — unknown condition is treated conservatively as not qualifying. This exclusion is not surfaced as a separate indicator in the response.
no_accident or no_frame_damage) and accident_grade_in are present in the same request, the engine intersects the two. A contradictory combination — for example no_accident: true with accident_grade_in: ["B"] — yields zero matches rather than a 400 error.
Buyer-fit tolerance (flexibility)
flexibility is an optional top-level block in the search and watch request body. It widens numeric filter bounds and expands categorical filter alternatives so a watch catches cars that fall just outside its strict criteria. When absent, all filters are applied strictly.
Flex matches are tagged match_type: "flex" in every response surface (search rows, match list items, and webhook entries). Strict matches carry match_type: "exact".
Request shape
Applies to all numeric and year filter fields that are present in the request and not overridden by
flexibility.fields. Does not apply to categorical fields (model, trim, fuel). Accepted values: none | small | medium | large. Default none.Per-field overrides. Each key must reference a filter field that is present in the same request — a key referencing an absent filter returns
400 eagle_eye_invalid_flexibility.Tolerance level for this field.
none | small | medium | large.For categorical fields (
model, trim, fuel) only. Each value creates one additional query variant. Required when level != 'none' on a categorical field. Maximum 4 values per field.Numeric and year tolerance
These fields accept alevel tolerance: year_min, year_max, mileage_max_km, price_max_krw, estimated_landed_max_usd, auction_count_min, auction_count_max.
| Level | Percent tolerance (max bounds) | Year slack |
|---|---|---|
none | 0% | ±0 years |
small | +2% (floor) | ±0 years |
medium | +5% (floor) | ±1 year |
large | +10% (floor) | ±2 years |
floor(value × (1 + tolerance)); min bounds apply ceil(value × (1 − tolerance)).
Santa Fe example — strict estimated_landed_max_usd: 13700 with level: "medium":
- Effective bound:
floor(13700 × 1.05) = 14385 - A vehicle priced at $13,782 is admitted and tagged
match_type: "flex" - A vehicle priced at $13,200 is admitted and tagged
match_type: "exact"
Categorical tolerance
Categorical fields (model, trim, fuel) do not have a numeric percentage tolerance. To widen them, supply allowed_values — each value creates one additional search variant that runs alongside the strict base query.
level must be none (explicit no-op) or paired with non-empty allowed_values. Supplying level: "small" (or any non-none level) without allowed_values on a categorical field returns 400 eagle_eye_invalid_flexibility.
Non-flexable fields
no_accident, no_frame_damage, source, and make only accept level: "none". This serves as an explicit per-field override of flexibility.default — useful to pin a boolean outside the default without setting a tolerance. Passing any other level for these fields returns 400 eagle_eye_invalid_flexibility.
Caps
| Limit | Value |
|---|---|
allowed_values per categorical field | 4 |
Total variant product (∏ (len(allowed_values) + 1) across all categorical fields) | 8 |
400 eagle_eye_invalid_flexibility with details.field naming the offending field.
Response metadata — match_type and flex_detail
Every match-bearing surface now carriesmatch_type, plus flex_detail describing any applied tolerance — surfaces differ on whether an exact match omits flex_detail or includes it as null, so see each surface’s example below for its exact-match shape. These fields are orthogonal to match_reason — match_reason tells you why a vehicle is in the watch (filters / signals); match_type / flex_detail tell you how closely it matched.
Search row
"exact" when all filters matched strictly. "flex" when at least one filter matched only within its tolerance.Present only when
match_type is "flex". One key per field that required the tolerance. Each value is either a numeric entry or a categorical entry:Numeric entry: { requested: number, level: string, effective: number, actual: number }Categorical entry: { requested: string, allowed_values: string[], actual: string }"match_type": "exact" with no flex_detail key.
Match list item
GET /v1/eagle-eye/watches/{watch_id}/matches now includes match_type and flex_detail on every row. Unlike search rows, match list items always include the flex_detail key; it is null for exact matches.
Webhook eagle_eye.match
additions and price_changes entries in the eagle_eye.match webhook now carry match_type and flex_detail:
flex_detail key (same omission behavior as search rows). removals entries are not changed — they carry only vehicle_id and reason.
Watch response
The watch resource (GET/POST/PUT /v1/eagle-eye/watches) now echoes back the stored flexibility configuration:
flexibility is null when not configured. A PUT request without a flexibility key clears it to null (full-replace semantics, same as signals).
Eagle Eye error codes
| Code | HTTP | When |
|---|---|---|
eagle_eye_filters_required | 400 | filters block absent; neither make nor model present; or make missing when source includes dealer. |
eagle_eye_invalid_filter_for_dealer | 400 | Auction-only filter present when source includes dealer (e.g., accident_grade_in, auction_count_min). |
eagle_eye_invalid_flexibility | 400 | flexibility block is structurally invalid: a field key references an absent filter, level != 'none' on a non-flexable field, allowed_values on a non-categorical field, categorical non-none level without allowed_values, or variant cap exceeded. details.field names the offending field. |
eagle_eye_invalid_signals | 400 | Signal bound or shape violation (e.g., min_listings out of range). |