DMC-12 dmc-12 / dmc-12 guide
UCP Merchant Binding · DMC-12 v1.0

Reservations

Three tools, one scope: reservation:write. A reservation is a 30-minute soft hold on a VIN, created from an open quote. It is not a binding purchase — a human closes the deal after hand-off.

Rules of the hold
  • One active hold per VIN (normative). A second create on a VIN that already has an unexpired active hold fails with RESERVATION_CONFLICT.
  • Server-side expiry (normative). Every hold carries expires_at, enforced server-side — an expired hold is rejected, never silently honored. The TTL value and the expiry mechanism (sweeper vs. lazy expiry) are deployment policy; the examples on this page use a 30-minute TTL.
  • Per-agent budget (normative that one exists; the value is deployment policy). Each deployment declares its own quota — e.g. the founding deployment ran 5 creates/day with a same-day-release refund. Creating is the only budget-consuming call in that pattern.

create_reservation

Convert an open quote into a soft hold. Validates that the quote exists, is open and unexpired, is owned by you, and that the vehicle is still available. On success the quote flips to converted.

FieldTypeConstraints
quote_idstringrequired10–64 chars — from request_quote
customer_refstringoptional≤ 200 chars — opaque; your own handle, not PII
// arguments
{ "quote_id": "qte_8f1c…", "customer_ref": "caredge-sess-91f3" }

// data
{ "reservation_token": "rsv_Qh7m…",
  "vin": "4S4BTGUD8R3201234",
  "reserved_price": "34187.00",
  "created_at": "2026-05-27T18:05:02+00:00",
  "expires_at": "2026-05-27T18:35:02+00:00",
  "status": "active",
  "terms": "30-minute soft hold. Not a binding purchase. A human closes the deal. …" }

Errors: QUOTE_NOT_FOUND, QUOTE_EXPIRED, QUOTE_STATE (already converted/closed), VEHICLE_UNAVAILABLE, RESERVATION_CONFLICT (VIN already held), RESERVATION_LIMIT (daily budget hit), NOT_AUTHORIZED (quote owned by another agent).

release_reservation

Release a hold you own. Idempotent — calling it on an already-released or expired reservation returns status with already_closed: true instead of an error, so it is always safe to call twice. Releasing within the same UTC day refunds 1 slot to your daily budget.

FieldTypeConstraints
reservation_tokenstringrequired10–80 chars
// arguments
{ "reservation_token": "rsv_Qh7m…" }

// data (first release)
{ "status": "released", "released_at": "2026-05-27T18:12:40+00:00", "vin": "4S4BTGUD8R3201234" }

// data (second, idempotent release)
{ "status": "released", "already_closed": true }

get_reservation_status

Look up the current state of a reservation you created. Same input shape as release.

FieldTypeConstraints
reservation_tokenstringrequired10–80 chars
// data
{ "reservation_token": "rsv_Qh7m…",
  "vin": "4S4BTGUD8R3201234",
  "reserved_price": "34187.00",
  "created_at": "2026-05-27T18:05:02+00:00",
  "expires_at": "2026-05-27T18:35:02+00:00",
  "released_at": null,
  "released_by": null,
  "status": "active" }

Status values: active, released, expired, converted. released_by is agent for an agent-initiated release. Errors: RESERVATION_NOT_FOUND, NOT_AUTHORIZED (owned by another agent).

Response fields

Shared across create_reservation, release_reservation, and get_reservation_status. See Data formats for the timestamp / Money / opaque-token conventions.

FieldTypeDescription
reservation_tokenstringcreate / statusOpaque hold handle (10–80 chars). Use it for release_reservation, get_reservation_status, and initiate_deal_handoff. Do not parse.
vinstringalwaysThe held VIN.
reserved_pricestring (exact-decimal, e.g. "34187.00")create / statusPrice locked at hold creation — the quote's quoted_price. Non-binding.
statusenumalwaysactive | released | expired | converted.
created_atstringcreate / statusISO-8601 UTC — when the hold was created.
expires_atstringcreate / statusISO-8601 UTC — created_at + 30 min. After this the hold lazy-expires.
released_atstring | nullstatus / releaseISO-8601 UTC when released; null while the hold is still active.
released_bystring | nullstatusagent for an agent-initiated release; null while active or if it expired untouched.
already_closedbooleanreleasetrue on an idempotent re-release of an already released/expired hold (no error). Absent on the first release.
termsstringcreateHuman-readable hold terms.

Next step

While a reservation is active you have a 30-minute window to either release it or run initiate_deal_handoff to bring a human into the loop. Let the window lapse and the hold expires automatically.

copied