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

Deal hand-off

initiate_deal_handoff · scope deal:handoff · the one tool that accepts customer PII. It converts an active reservation into an emailed deal-intent package for a live sales manager. PII is hashed in every audit/DB surface; plaintext flows only to the dealer's inbox. Requires explicit customer_consent: true.

🔒 What "hashed" means here Fields marked 🔒 hashed are SHA-256'd before they touch the audit log or the deal_handoffs table. The only place their plaintext lands is the outbound email to the dealer desk (and the merchant's CRM lead, when enabled). Structured trade-in fields and consent channels/expiry are plaintext — they're not PII.
Consent gates contact as of v1.0 When the optional consent object is present, its allowed_channels set restricts how the merchant may contact the customer — follow-up on a channel outside the authorized set is non-conformant (SPEC MC-7 / TT-5). Sending only the bare customer_consent: true boolean records no channel restriction. expires_at is enforced: an expired consent is rejected with CONSENT_INVALID before anything is sent.

Full field reference

FieldTypeConstraint · PII
reservation_tokenstringreq10–80 chars
customer_contact.namestringreq1–120 · 🔒
customer_contact.phonestringreqE.164, 8–20 · 🔒
customer_contact.emailstringreq5–320 · 🔒
customer_consentboolreqconst true
financing_preferenceenumrequiredcash|finance|lease|unknown — no default; send unknown when unstated
notesstringopt≤500 · 🔒
trade_in_disclosedboolrequiredno default — true whenever any trade_in field is sent
trade_inobjectoptextra fields forbidden
trade_in.descriptionstringopt≤500 · 🔒
trade_in.appraised_value_partnerMoneyopt{amount: exact-decimal string "^[0-9]+\.[0-9]{2}$", currency ^[A-Z]{3}$} · 💲
trade_in.vinstringopt^[A-HJ-NPR-Z0-9]{17}$
trade_in.yearintopt1900–2100
trade_in.makestringopt≤40
trade_in.modelstringopt≤80
trade_in.mileageintopt0–1,000,000
trade_in.conditionenumoptexcellent|good|fair|poor
consentobjectoptextra fields forbidden
consent.allowed_channelsenum[]opt≥1 unique of email|phone|sms · gating (v1.0)
consent.expires_atdatetimeopttz-aware · enforced (CONSENT_INVALID if past)
consent.consent_textstringopt≤2000 · 🔒

Worked examples

Minimal — bare boolean consent

{
  "reservation_token": "rsv_Qh7m…",
  "customer_contact": { "name": "Jane Doe", "phone": "+15555551234", "email": "jane@example.com" },
  "financing_preference": "unknown",
  "trade_in_disclosed": false,
  "customer_consent": true
}

+ trade_in

{
  "reservation_token": "rsv_Qh7m…",
  "customer_contact": { "name": "Jane Doe", "phone": "+15555551234", "email": "jane@example.com" },
  "customer_consent": true,
  "financing_preference": "finance",
  "trade_in_disclosed": true,
  "trade_in": {
    "description": "Single owner, clean Carfax",
    "appraised_value_partner": { "amount": "18500.00", "currency": "USD" },
    "vin": "JF2GTACC4K8200000", "year": 2019, "make": "Subaru",
    "model": "Crosstrek", "mileage": 48000, "condition": "good"
  }
}

+ consent (channel-scoped)

{
  "reservation_token": "rsv_Qh7m…",
  "customer_contact": { "name": "Jane Doe", "phone": "+15555551234", "email": "jane@example.com" },
  "financing_preference": "unknown",
  "trade_in_disclosed": false,
  "customer_consent": true,
  "consent": {
    "allowed_channels": ["email", "phone"],
    "expires_at": "2026-06-01T00:00:00Z",
    "consent_text": "Customer authorized follow-up by email and phone on 2026-05-27."
  }
}

What the dealer receives

On success the tool emails a deal-intent package to the dealer desk and returns a masked receipt — no plaintext customer data in the response:

{ "data": {
    "handoff_token": "hnd_xyz…",
    "sent_to_masked": "sa****@dealer.example, gm****@dealer.example",
    "sent_at": "2026-05-27T18:22:01+00:00",
    "status": "sent",
    "crm_status": "sent",        // "sent" | "failed" | "disabled"
    "crm_lead_id": "hnd_xyz…",
    "next_steps": "A sales manager will contact ja****@example.com within 1 business day…"
  },
  "_metadata": { "trace_id": "trc_…", "tool": "initiate_deal_handoff", "protocol": "a2a" }
}

Response fields

The masked receipt. No plaintext customer data is ever returned. See Data formats for timestamp / enum conventions.

FieldTypeDescription
handoff_tokenstringalwaysOpaque receipt handle for the recorded hand-off. Do not parse.
sent_to_maskedstringalwaysComma-separated, masked dealer-desk recipients (e.g. be****@…) — confirms where the package went without exposing addresses.
sent_atstringalwaysISO-8601 UTC — when the dealer-ops email sent (the moment the hand-off became real).
statusenumalwayssent on success. The email is the contract — a send failure returns an error, not a status.
crm_statusenumalwayssent | failed | disabled. Best-effort CRM (e.g. ADF 1.0) lead outcome; disabled means the CRM sink is off. A failed CRM does not fail the call.
crm_lead_idstring | nulloptionalCRM lead identifier when a lead was created; null/absent when crm_status is failed or disabled.
next_stepsstringalwaysHuman-readable follow-up summary, with the customer email masked.
Ownership & expiry The reservation must be active and owned by your agent. Common errors: RESERVATION_NOT_FOUND, RESERVATION_STATE, RESERVATION_EXPIRED, NOT_AUTHORIZED (token owned by another agent), CONSENT_INVALID (supplied consent.expires_at in the past). Full list on the error taxonomy page.
copied