Order snapshot in data.order so your handler can act without a follow-up GET. The order invoice events carry the full Invoice plus the same Order snapshot. 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, current_max_bid_usd, and — since v1.60 — the invoice summary). The same holds for data.order on order.invoice_issued. 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.detail_updated
Fires when an existing order is materially updated: prices, VIN, plate,
options, inspection information, shipment, or other order details. Changes to a
competing bid that affects the order snapshot also notifies it. Invoice edits use
order.invoice_updated instead.
This is an Order event; it does not replace Eagle Eye watch notifications.
data.order is the full current partner-facing Order, including its invoice summary
and vehicle snapshot. Admin-only fields are never included. Admin/API/background
writes and data repairs are captured. No-op and timestamp-only writes are silent;
offline orders remain silent. Creation alone is not a detail update.
Rapid edits can combine into one latest-state notification on the next delivery
worker run. This is not a history of every intermediate edit. It has its own event
ID and uses the normal signing, retry, and replay rules. Existing events such as
order.status_changed and order.invoice_issued cover their own lifecycle operations:
status transitions and invoice issue/reissue do not also queue this event.
Automatic draft creation during a status transition is included in the status event.
Separate detail edits remain eligible; handle snapshots idempotently. occurred_at is the last captured change
time, which may differ from order.updated_at when a related invoice changes.
Replay always resends the saved snapshot rather than rebuilding it.
Abbreviated example (the actual order contains the complete Order resource):
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 order.status_changed. Status transitions and creation alone do not queue order.detail_updated. 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:
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 competingplacedorders on the same auction lot enteracquiringtogether before resolution, so each competitor sees a cleanplaced → acquiring → failedsequence.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 atauction_date + 24h(see below).seller_withdrew— seller withdrew the car after LMN started attempting to acquire.
Zombie-cleanup variant
If LMN never picked up aplaced 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):
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 sentDELETEwhileplaced.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 atsecondary_inspection_ready(inspection found disqualifying issues); this one firesorder.status_changed.secondary_inspection_declined— partner sentDELETEwhilesecondary_inspection_ready. Partner-initiated, but this decision does fireorder.status_changed(previous_status: secondary_inspection_ready, amended 2026-07-06) in addition to being echoed onGET /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.
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.
delta_usdis negative when the price dropped (partner-friendly).delta_pctis rounded to 1 decimal place and isnullifprevious_listing_usdwas 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-acquiringorders 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.
vehicle_id back to the dealer UI and let them re-POST /v1/orders if still interested.
Semantics:
- The attached
data.orderis 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_idis the ID from the terminal order;data.new_vehicle_idis the current listing’s ID.data.new_auction_dateis in KST ISO 8601 with+09:00offset.
order.invoice_updated
Fires when an order’s invoice changes outside issue/reissue: manual draft creation,
draft edits (including consignee, terms, and amounts), payment recording, or voiding.
The payload contains the full partner-safe data.order and current data.invoice.
No-op, timestamp-only, and admin-only changes are silent. Rapid edits may combine
into one latest-state notification. This does not also emit order.detail_updated.
Automatic draft creation during a status transition is covered by order.status_changed.
Issue and reissue emit only order.invoice_issued for the publication operation.
For Shekel’s 1:1 order/invoice flow, key these events by data.order.id. The invoice
resource retains its invoice ID and version. Existing saved invoice.issued events
retain their original payload and type when replayed; new publications use the new name.
order.invoice_issued
Fires when LMN issues the commercial invoice for an order, and again on every re-issue. data.invoice is the full Invoice exactly as published for that version — its document_url is pinned to ?version=N and keeps returning that version’s PDF after later re-issues. data.order is the full Order snapshot at publication, including the nested invoice summary (parity with GET /v1/orders/{id}). Always on; no opt-in. See Invoices for the lifecycle.
Re-issue
Same event type, samenumber, version + 1; data.previous_version is the version being replaced. Here LMN completed the packing list after loading:
previous_versionisnullon version 1 andversion − 1afterwards.issued_atis the first publication and does not move on re-issue.- Dedupe on the envelope
id. To keep one record per invoice, key ondata.invoice.numberand replace your copy whendata.invoice.versionis higher than the one you hold; ignore equal or lower versions (deliveries are at-least-once and may arrive out of order). - Not fired for draft creation,
paid, orvoid— those emitorder.invoice_updated(except automatic draft creation during a status transition), and remain readable viaOrder.invoiceand the Invoices routes. - Delivery: identical signing, retry, and replay semantics as every other event — see signing and retries. The event row is written in the same transaction as the invoice version, so an issued version is never missing its event.
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.
Relisted auction vehicles
A confirmed relist reuses this event and appears as a fresh entry indata.additions[]; there is no eagle_eye.relisted event. When LMN can link
the new auction occurrence to a prior one by exact normalized VIN, the
addition includes these optional fields together:
availability_reason: "relisted"previous_vehicle_id: the latest strictly earlier listing occurrence on this watch
watch_id + the new vehicle id), so each later auction can
produce one fresh addition.
Signal detail
Signal-driven matches includesignal_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), explicit membership changes still happen 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.
Auction static membership also follows the same physical car into a later auction when an exact, unambiguous VIN link exists. LMN replaces the completed listing id with the new occurrence as one logical membership and emits a relisted additions entry. A hidden member follows silently and remains hidden. Missing or ambiguous VIN leaves the member pinned to the old occurrence; LMN never guesses from make/model or plate alone.
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.
external_user_idis the id your widget JWT supplied for this dealer — use it to route the alert to the right user. It can benull(the dealer-identity lookup is defensive and the event still fires if it misses) — null-check before routing and fall back tosession_id.display_namecan also benullfor the same reason asexternal_user_idabove.session_idis the chat thread (one per dealer) — the openable unit for a “you have a new message” link. Use this as your routing fallback whenexternal_user_idisnull.message_idis ULID-ordered — use it for ordering and unread cursors, notoccurred_at.text_previewis 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_blockstells you a rich card (e.g. a vehicle match list) accompanied the text but isn’t itself serialized here.kindisassistantfor AI agent replies orstafffor a human operator reply (takeover mode). Both are treated the same way for alerting purposes.origincan benullfor messages that predate the widget-origin field; treat anullorigin as unknown surface, not an error.directionis alwaysoutboundtoday — reserved for future inbound/system message types.
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.session_idis the dealer’s chat room — the deep-link/routing target, same role it plays inchat.message.created.external_user_idcan benull— dealer identity is resolved defensively by(chat_session_id, partner_id, environment); a missing or ambiguous match never guesses a dealer, it emitsnullinstead. Route bysession_idwhen null.display_namecan also benullfor the same reason.watch_id/watch_nameidentify the Eagle Eye watch that changed;watch_nameisnullfor unnamed watches.chat_message_idis 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_idcorrelates this ping to theeagle_eye.matchwebhook delivery that triggered it. No vehicle payload is duplicated here — the fulladditions/price_changes/removalsentries live on that event; join on this id to fetch them.countsis a summary only —additions,price_changes,removalsmirror the lengths of the corresponding arrays on the pairedeagle_eye.matchevent.
id.