Skip to main content
Order events carry a full Order snapshot in data.order so your handler can act without a follow-up GET. Eagle Eye events carry the watch context plus the vehicles or vehicle IDs that changed. Chat events carry a preview of a dealer-visible chat message so you can alert the dealer without polling the widget stream.
Order payload parity (v1.18+): the data.order field in every order webhook is structurally identical to the response of GET /v1/orders/{id} — same field names, types, nullability, and enrichment (including is_highest_bid and current_max_bid_usd). You can reuse the same Order type and deserializer across order reads and order webhooks.Eagle Eye vehicle parity: vehicle objects inside eagle_eye.match additions and price_changes use the same VehicleSummary schema returned by GET /v1/vehicles. Eagle Eye adds watch and signal metadata around that vehicle object; it does not define a separate vehicle model.Examples below are abbreviated for brevity. Order webhook payloads include every field documented under Get an order, not only the ones shown here. Eagle Eye vehicle examples are also abbreviated; build against the shared VehicleSummary schema.

order.status_changed

Fires on every server-side status transition. Includes the previous status for reconstruction.

Status transitions that fire this event

secondary_inspection_ready is a decision point with two possible drivers: admin or the partner directly (confirm/decline). Both drivers fire this webhook — the partner’s event pipe carries every outcome of the decision point (amended 2026-07-06; see below). Partner-initiated transitions (placed creation, placed → cancelled via DELETE, in_transit → customs, customs → delivered) are confirmed by the API response and do not fire webhooks. The exception is the secondary_inspection_ready decision point: secondary_inspection_ready → acquiring via POST /v1/orders/{id}/confirm and secondary_inspection_ready → cancelled via DELETE /v1/orders/{id} (cancellation_reason: secondary_inspection_declined) do fire order.status_changed (previous_status: secondary_inspection_ready), in addition to the synchronous API response.

Inspection-ready example (v1.49)

When the inspection completes, the ready-transition event carries the LMN URL of the inspection report (data.order.secondary_inspection_url, nullable) so you can surface it to your dealer at the confirm/decline decision point:
The URL stays on the order afterwards (GET /v1/orders/{id} and later webhooks include it too). It is null when no report link was recorded at the transition.

Failed-auction example

failure_reason enum (terminal failed, reachable from acquiring only):
  • outbid — lost to external bidder at auction.
  • outbid_internally — another partner’s higher bid won. All competing placed orders on the same auction lot enter acquiring together before resolution, so each competitor sees a clean placed → acquiring → failed sequence.
  • vehicle_unavailable — LMN’s dealer purchase attempt failed (car sold to another buyer).
  • auction_cancelled — auction event cancelled after LMN began bidding.
  • auction_passed — vehicle passed at auction, OR zombie cleanup at auction_date + 24h (see below).
  • seller_withdrew — seller withdrew the car after LMN started attempting to acquire.

Zombie-cleanup variant

If LMN never picked up a placed order and the auction has been over for 24 hours, the system auto-resolves it with failure_reason: auction_passed. The webhook collapses the synthetic intermediate state — previous_status is placed (not acquiring):
Treat this as a regular failed event. You can recognize the zombie-cleanup variant by the combination previous_status: "placed" + failure_reason: "auction_passed" + acquired_at: null — a normally-failed auction order transitions from acquiring, not placed.

Cancellation example

cancellation_reason enum (terminal cancelled, reachable from placed or secondary_inspection_ready):
  • dealer_cancelled — partner sent DELETE while placed.
  • auction_rescheduled_too_soon — LMN auto, auction rescheduled past order cutoff.
  • auction_cancelled — LMN auto, auction event cancelled before LMN started bidding.
  • seller_withdrew — LMN auto, seller pulled dealer listing before LMN started purchase.
  • secondary_inspection_failed — LMN/admin cancelled at secondary_inspection_ready (inspection found disqualifying issues); this one fires order.status_changed.
  • secondary_inspection_declined — partner sent DELETE while secondary_inspection_ready. Partner-initiated, but this decision does fire order.status_changed (previous_status: secondary_inspection_ready, amended 2026-07-06) in addition to being echoed on GET /v1/orders/{id} and in the DELETE response.
failure_reason and cancellation_reason are mutually exclusive — an order has one or the other, never both. For reasons that can appear in either enum (auction_cancelled, seller_withdrew), the order’s current status at the moment of the upstream event decides the terminal.

order.auction_rescheduled

Fires when the auction source postpones or advances an auction, shifting auction_date and recomputing order_cutoff_at. No status change.
Compare data.previous_auction_date against data.order.auction_date to detect direction and magnitude. Update dealer-facing deadlines accordingly. If a reschedule moves auction_date so close that order_cutoff_at is already in the past on a placed order, LMN auto-cancels with cancellation_reason: "auction_rescheduled_too_soon" and fires a separate order.status_changed event. Orders already in acquiring or later are unaffected (LMN is committed; the auction proceeds on the new date).

order.price_updated

Fires when the vehicle backing an active placed order has its listing_price updated by the upstream scraper. No status change. The payload keys (previous_listing_usd / current_listing_usd) keep the _usd suffix for webhook-event convention; they refer to the same underlying value as pricing.listing_price on the vehicle. Useful for partners whose bid strategy depends on the current market estimate — a price drop may mean the dealer should raise max_bid_amount_usd to stay competitive, while a spike may mean the current max_bid is now insufficient.
Semantics:
  • delta_usd is negative when the price dropped (partner-friendly).
  • delta_pct is rounded to 1 decimal place and is null if previous_listing_usd was 0.
  • Fires at most once per actual change — identical refreshes to the same value are deduped server-side.
  • Fires only while the order is in placed — terminal-state and post-acquiring orders ignore market refreshes.

order.re_auctioned

Fires when a vehicle tied to one of your terminal orders (status failed / cancelled / delivered) reappears in a new auction round. Matched by Korean license plate across auction houses, so the same physical car is caught even if it moves from Glovis to SK or Lotte between listings.
Use case: the dealer originally bid, lost (or cancelled), and wants a second chance. On receiving this event, surface the new vehicle_id back to the dealer UI and let them re-POST /v1/orders if still interested. Semantics:
  • The attached data.order is the original terminal order — NOT a new order. No new order is implicitly created.
  • Fires at most once per (license_plate, new_auction_date) — no duplicates during re-scrapes.
  • data.previous_vehicle_id is the ID from the terminal order; data.new_vehicle_id is the current listing’s ID.
  • data.new_auction_date is in KST ISO 8601 with +09:00 offset.

eagle_eye.match

Fires when a saved Eagle Eye watch has at least one addition, price change, or removal. The payload is batched per watch and includes enough context for a partner UI to explain why the vehicle appeared. Test events: a sandbox test fire (POST /v1/eagle-eye/watches/{watch_id}/test-webhook, or the console’s “Send test webhook” action) delivers this same event type with data.is_test: true added. The key is present on every test event and never on organic ones — check it before downstream automation acts on an addition. See Eagle Eye → Testing your receiver.

Signal detail

Signal-driven matches include signal_detail so product teams can show the dealer why the alert matters: price_drop is drop-only. A price increase does not carry signals.price_drop; if the vehicle still qualifies, the current match updates silently and no price_changes webhook entry is emitted. If the increase makes it fail the watch’s filters, LMN sends a removal.

Static lists

The rule above — drop-only, no emission on NULL→value, no emission on rises — applies only to automatic watches (automatic_list_update: true). On a static list (automatic_list_update: false), membership only changes through POST …/matches / POST …/matches/remove (no webhook fires for those calls — you performed the action yourself). Once a car is a member, every price transition LMN observes on it emits via this webhook: a price appearing on a previously un-priced listing (NULL→value — the core static-list use case), a drop, and a rise. These entries carry match_reason: ["manual"] instead of filters / signals.*, and never carry signal_detail — signals cannot be configured on static lists. Static lists never produce additions entries — LMN only monitors cars you’ve explicitly added; it never adds new ones on its own. A non-hidden static-list member that disappears from live inventory (sold/delisted) still produces a removals entry with reason: "auction_complete", same as an automatic watch. See Eagle Eye endpoints — Static lists for how cars enter and leave a static list.

Removal reasons

under_contract is the only reversible reason, and it’s static-list-only. Contracts fall through. When that happens, a static-list member reappears in the match list as a new entry in additions on this same eagle_eye.match event — there is no separate event type for a revival. On an automatic (filter-based) watch, the same real-world event still removes the dealer car from the match list, but is reported as reason: "delisted" — automatic watches don’t distinguish a contract from a genuine sale; the car can still come back as a plain new additions entry once it’s purchasable again, just without the distinct reason label on the way out. Either way, don’t treat a dealer-car removal as necessarily final. This list is not exhaustive. Handle an unrecognized reason defensively (per conventions) rather than with a switch that has no default case — LMN may add reasons over time. Hiding a match via POST /v1/eagle-eye/watches/{watch_id}/matches/hide is a view-state change on your side only — it never produces a removals entry on this webhook. Use GET /v1/eagle-eye/watches/{watch_id}/events to recover the exact webhook payload history for a watch.

chat.message.created

Fires when LMN produces a dealer-visible message in your embedded chat — an AI agent reply or a human staff reply. One event per message. A question that triggers an inventory search may produce two events in quick succession: a brief interim acknowledgement (e.g. “Let me check inventory…”) followed shortly by the full reply. Receivers should expect this and not treat it as a duplicate.
Off by default. This event is high-volume, so it isn’t sent unless your integration has subscribed. Contact LMN to enable it.
Field notes:
  • external_user_id is the id your widget JWT supplied for this dealer — use it to route the alert to the right user. It can be null (the dealer-identity lookup is defensive and the event still fires if it misses) — null-check before routing and fall back to session_id.
  • display_name can also be null for the same reason as external_user_id above.
  • session_id is the chat thread (one per dealer) — the openable unit for a “you have a new message” link. Use this as your routing fallback when external_user_id is null.
  • message_id is ULID-ordered — use it for ordering and unread cursors, not occurred_at.
  • text_preview is capped at 240 characters and never includes tool payloads or UI block data. The full conversation is available to the dealer in the widget; has_ui_blocks tells you a rich card (e.g. a vehicle match list) accompanied the text but isn’t itself serialized here.
  • kind is assistant for AI agent replies or staff for a human operator reply (takeover mode). Both are treated the same way for alerting purposes.
  • origin can be null for messages that predate the widget-origin field; treat a null origin as unknown surface, not an error.
  • direction is always outbound today — reserved for future inbound/system message types.
Delivery: identical signing, retry, and idempotency semantics as every other event — see signing and retries. Deliveries are at-least-once, so dedupe on the envelope id. Ordering across retries is not guaranteed; order by data.message_id, not by delivery arrival.

chat.eagle_eye.match_posted

Fires after LMN posts an Eagle Eye match digest into a dealer’s chat room — the same room-visible message the dealer already sees in the widget. Only fires for chat-created (room-associated) watches: watches built through a chat proposal (create_watch / update_watch), which carry a chat_session_id. API-created watches (created directly via POST /v1/eagle-eye/watches) have no room and remain webhook-only — this event never fires for them; they continue to receive eagle_eye.match directly, same as today.
Off by default. Like chat.message.created, this event isn’t sent unless your integration has subscribed. Contact LMN to enable it.
Field notes:
  • session_id is the dealer’s chat room — the deep-link/routing target, same role it plays in chat.message.created.
  • external_user_id can be null — dealer identity is resolved defensively by (chat_session_id, partner_id, environment); a missing or ambiguous match never guesses a dealer, it emits null instead. Route by session_id when null.
  • display_name can also be null for the same reason.
  • watch_id / watch_name identify the Eagle Eye watch that changed; watch_name is null for unnamed watches.
  • chat_message_id is the id of the digest message already posted into the room (visible to the dealer in the widget) — use it to deep-link straight to that message, or to dedupe against your own read of the room.
  • match_event_id correlates this ping to the eagle_eye.match webhook delivery that triggered it. No vehicle payload is duplicated here — the full additions / price_changes / removals entries live on that event; join on this id to fetch them.
  • counts is a summary only — additions, price_changes, removals mirror the lengths of the corresponding arrays on the paired eagle_eye.match event.
Delivery: identical signing, retry, and idempotency semantics as every other event — see signing and retries. Deliveries are at-least-once; dedupe on the envelope id.