Automotive Commerce Protocol — Normative specification defining the JSON envelope format for AI-generated automotive commerce data. This document describes the structure, constraints, and semantics of ACP envelopes. It does not describe the internal methods by which producers generate envelope contents.
The following topics are explicitly outside the scope of this specification and must not be inferred from the data format definitions herein:
lead.score and score_factors are calculated. The specification defines the output format only.lead.intents[] are identified and confidence values are derived.conversation.handoff to be initiated.The key words "must", "must not", "required", "shall", "shall not", "should", "should not", "recommended", "may", and "optional" in this document are to be interpreted as described in RFC 2119.
| Keyword | Meaning |
|---|---|
| MUST | Absolute requirement. Non-compliance renders the implementation non-conformant. |
| MUST NOT | Absolute prohibition. |
| SHOULD | Recommended. Valid reasons may exist to ignore, but full implications must be understood. |
| SHOULD NOT | Discouraged. Valid reasons may exist to allow, but full implications must be understood. |
| MAY | Truly optional. Implementations may or may not include this behavior. |
Producers must set the HTTP Content-Type header to application/acp+json when transmitting envelopes. Receivers must accept application/acp+json and should also accept application/json for interoperability.
Monetary fields (vehicle_price, monthly_payment, trade_in_value, down_payment, estimated_value, payoff_amount)
should be expressed as decimal values with exactly two fractional digits (e.g., 29995.00, not 29995 or 29995.999).
Percentage fields (apr, purchase_probability, confidence) should be expressed as decimal values with appropriate precision for the domain.
Receivers must not assume floating-point precision beyond standard IEEE-754 double-precision limits. Producers should not emit monetary values with more than two fractional digits.
An ACP envelope is a single JSON document with eight top-level objects. Seven are required for snapshot envelopes. Delta envelopes must contain only acp +
events.
{
"acp": { }, // Protocol metadata — REQUIRED
"source": { }, // Origin attribution — REQUIRED
"dealership": { }, // Receiving dealer — REQUIRED
"lead": { }, // Customer intelligence — REQUIRED
"transaction": { }, // Deal state — REQUIRED
"conversation": { }, // Transcript — REQUIRED
"events": [ ], // Behavioral stream — REQUIRED
"ai_analysis": { } // AI insights — OPTIONAL
}
Producers must not include additional top-level keys beyond these eight. Receivers must ignore unknown top-level keys without error.
Producers should limit snapshot envelopes to 1MB uncompressed. If conversation transcripts exceed this limit, producers should truncate conversation.messages to
the most recent messages and include a vendor-namespaced archival reference field (e.g., x_vendor.transcript_archive_url) pointing to the complete transcript.
Receivers must not reject envelopes solely based on size, but may return PAYLOAD_TOO_LARGE (HTTP 413) if the envelope exceeds their ingestion capacity. Receivers
should document their maximum accepted payload size.
Delta envelopes are typically much smaller than snapshots — often under 10KB — as they contain only the changed fields and new events.
JSON object key ordering is not semantically meaningful in ACP. Implementations must not depend on the order of keys within any object. Producers may emit keys in any order.
Receivers must process envelopes identically regardless of key ordering. Array ordering is significant: events must be chronologically ordered, and
vehicle_interests should be ordered by customer preference (most preferred first).
acp — Protocol Metadata REQUIREDIdentifies protocol version, envelope identity, and generation context. Uses additionalProperties: false — producers must not add vendor fields to this object.
| Field | Type | Req | Description |
|---|---|---|---|
| version | string | MUST | Semantic version. Pattern: ^\d+\.\d+\.\d+$. Producers must set to "1.0.0" for this specification. |
| protocol | const "acp" | MUST | Protocol identifier. Must be "acp". |
| generated_at | string (date-time) | MUST | ISO 8601 UTC timestamp of envelope generation. |
| envelope_id | string | MUST | Globally unique idempotency key. Must match pattern ^acp_. Receivers must use this for deduplication. Receivers should retain
processed envelope_id values for a minimum of 30 days to ensure reliable deduplication across retries and network failures. |
| parent_envelope_id | string | null | MAY | For delta envelopes: references the most recent snapshot. When non-null, all other top-level objects must not be present. |
{
"acp": {
"version": "1.0.0",
"protocol": "acp",
"generated_at": "2025-02-13T15:30:00Z",
"envelope_id": "acp_1739456200_x8k2m9",
"parent_envelope_id": null
}
}
source — Origin Attribution REQUIREDIdentifies who generated this envelope, from what channel, and how the customer arrived. Extensible — vendors may add custom fields.
| Field | Type | Req | Description |
|---|---|---|---|
| provider | string | MUST | Generating platform name. |
| agent_type | string | MUST | "sales_chat", "sales_voice", "inbound_voice", "service_text", "service_voice" |
| channel | string | MUST | "web_chat", "phone", "sms", "email", "voice" |
| session_id | string | MAY | Unique session identifier for cross-channel linking. |
| device | object | MAY | Device context: type, browser, os, ip. |
| utm | object | MAY | Marketing attribution: source, medium, campaign, term, content. |
| page_url | string | MAY | URL where the conversation was initiated. |
| referrer | string | MAY | HTTP referrer URL. |
dealership — Receiving Dealer REQUIREDIdentifies the dealership that should receive this envelope. Receivers must use id for routing.
| Field | Type | Req | Description |
|---|---|---|---|
| id | string | MUST | Unique dealership identifier in the producer's system. |
| name | string | MUST | Dealership display name. |
| crm_provider | string | MAY | "salesforce", "vinsolutions", "dealersocket" |
| dms_provider | string | MAY | "cdk", "reynolds", "tekion" |
| timezone | string | MAY | IANA timezone. e.g. "America/New_York" |
lead — Customer Intelligence REQUIREDThe core customer data object. Producers must include id and status. All other fields are optional but recommended for maximum CRM utility.
| Field | Type | Req | Description |
|---|---|---|---|
| id | string | MUST | Unique lead identifier. |
| status | string | MUST | "new", "qualified", "contacted", "converted", "lost" |
| contact | object | SHOULD | Customer contact data. |
| vehicle_interests | array | SHOULD | Vehicles discussed. Should be ordered by preference. |
| budget | object | MAY | Customer budget parameters. |
| trade_in | object | MAY | Trade-in vehicle details. |
| intents | array | SHOULD | Detected purchase intents with confidence scores. |
| score | integer (0–100) | MAY | Lead qualification score. Producer-defined algorithm. |
| score_factors | array | MAY | Producer-supplied breakdown. See note below. |
score and score_factors are produced by
the generating platform's proprietary algorithms. This specification does not define how scores are calculated, what thresholds trigger status changes, or what weighting models are used. Producers may use any
scoring methodology. The format exists so that CRM receivers can display and act on scores regardless of which producer generated them.| Field | Type | Description |
|---|---|---|
| first_name | string | Customer first name. |
| last_name | string | Customer last name. |
| string | Email address. Producers should validate format. | |
| phone | string | Phone number. E.164 format recommended. |
| zip | string | ZIP or postal code. |
| address | object | Full address: street, city, state, zip, country. |
| Field | Type | Description |
|---|---|---|
| year | integer | Model year. |
| make | string | Manufacturer. |
| model | string | Model name. |
| trim | string | Trim level. |
| condition | string | "new", "used", "certified" |
| color | string | Preferred exterior color. |
| stock_number | string | Dealership stock number. |
| vin | string | VIN if referencing a specific vehicle. |
| features | array<string> | Requested features. |
| Field | Type | Description |
|---|---|---|
| target_price | number | Customer's target purchase price. |
| max_price | number | Maximum willing to spend. |
| monthly_payment_target | number | Desired monthly payment. |
| down_payment | number | Down payment available. |
| Field | Type | Description |
|---|---|---|
| year | integer | Trade-in model year. |
| make | string | Manufacturer. |
| model | string | Model. |
| mileage | integer | Current mileage. |
| condition | string | "excellent", "good", "fair", "poor" |
| estimated_value | number | Customer's estimated value. |
| payoff_amount | number | Remaining loan balance. |
Each item represents a detected purchase intent. The detection methodology is producer-defined; this specification defines only the output format.
| Field | Type | Req | Description |
|---|---|---|---|
| type | string (enum) | MUST | Intent classification. See standard types below. |
| confidence | number (0–1) | MUST | Detection confidence. Producer-defined scale. |
| detected_at | date-time | SHOULD | When this intent was first detected. |
| source_message_id | string | MAY | Message that triggered detection. |
Producers should use standard types where applicable. Vendors may define additional types prefixed with their namespace: "x-vendorname:custom_intent". Receivers
must not reject envelopes containing unknown intent types.
| Field | Type | Description |
|---|---|---|
| factor | string | Human-readable factor name. Producer-defined. |
| points | integer | Points added (positive) or deducted (negative). |
| category | string | "contact", "intent", "engagement", "qualification", "negative" |
transaction — Deal State REQUIREDModels the commerce lifecycle. Producers must include id, stage, and last_updated.
| Field | Type | Req | Description |
|---|---|---|---|
| id | string | MUST | Unique transaction identifier. |
| stage | string (enum) | MUST | Current lifecycle stage. |
| last_updated | date-time | MUST | When transaction state last changed. |
| offer_history | array | MAY | Chronological offer revisions. |
| finance_terms | object | MAY | Financing details. |
| appointment | object | MAY | Scheduled interaction. |
| delivery | object | MAY | Delivery confirmation. |
closed_lost stage is terminal — producers must create a new transaction if the customer re-engages.Uses additionalProperties: false. Vendors must not add custom fields.
| Field | Type | Req | Description |
|---|---|---|---|
| revision | integer | MUST | Revision number (starts at 1). |
| timestamp | date-time | MUST | When this offer was made. |
| status | string | MUST | "pending", "accepted", "rejected", "countered", "expired" |
| vehicle_price | number | MAY | Offered vehicle price. |
| trade_in_value | number | MAY | Trade-in valuation offered. |
| monthly_payment | number | MAY | Monthly payment quoted. |
| notes | string | MAY | Agent notes about this offer. |
| Field | Type | Description |
|---|---|---|
| type | string | "finance", "lease", "cash" |
| term_months | integer | Loan or lease term in months. |
| apr | number | Annual percentage rate. |
| down_payment | number | Down payment amount. |
| monthly_payment | number | Calculated monthly payment. |
| credit_tier | string | Customer's credit classification. |
| Field | Type | Description |
|---|---|---|
| type | string | "test_drive", "sales_visit", "delivery", "service" |
| scheduled_at | date-time | Appointment date and time. |
| status | string | "scheduled", "confirmed", "completed", "cancelled", "no_show" |
| assigned_to | string | Assigned sales rep name. |
| Field | Type | Description |
|---|---|---|
| status | string | "pending", "in_transit", "delivered" |
| scheduled_at | date-time | Scheduled delivery date. |
| delivered_at | date-time | Actual delivery timestamp. |
| vin | string | VIN of the delivered vehicle. |
conversation — Transcript REQUIREDProducers must include id and started_at. Including messages is recommended.
| Field | Type | Req | Description |
|---|---|---|---|
| id | string | MUST | Unique conversation identifier. |
| started_at | date-time | MUST | Conversation start timestamp. |
| ended_at | date-time | MAY | End timestamp. Null if active. |
| duration_seconds | integer | MAY | Total conversation duration. |
| message_count | integer | SHOULD | Total messages exchanged. |
| messages | array | SHOULD | Full transcript. |
| handoff | object | MAY | AI-to-human escalation record. |
| sentiment | string | MAY | "positive", "neutral", "negative", "mixed" |
| topics | array<string> | MAY | Topics discussed. |
Messages must be chronologically ordered. Producers must not alter message content after generation.
| Field | Type | Req | Description |
|---|---|---|---|
| id | string | MUST | Unique message identifier. |
| role | string (enum) | MUST | "user" (customer), "assistant" (AI), "system" |
| content | string | MUST | Message text content. |
| timestamp | date-time | MUST | When the message was sent. |
| metadata | object | MAY | Message-level metadata. Vendor-extensible. |
Records that a handoff was initiated and why. The decision logic for triggering handoffs is producer-defined; this object captures the outcome only.
| Field | Type | Description |
|---|---|---|
| requested | boolean | Whether a handoff was triggered. |
| reason | string | "customer_request", "emotional", "price_authority", "complexity", "complaint", "deal_ready" |
| priority | string | "urgent", "high", "normal", "low" |
| summary | string | Human-readable summary enabling seamless continuation. |
| triggered_at | date-time | When handoff was initiated. |
events — Behavioral Stream REQUIREDChronological array of typed behavioral events. Producers must preserve chronological order. Receivers must not reorder events.
| Field | Type | Req | Description |
|---|---|---|---|
| type | string | MUST | Event classification. |
| timestamp | date-time | MUST | When the event occurred. |
| data | object | MAY | Event-specific payload. Schema varies by type. |
Producers should use standard types where applicable. Custom event types must be namespaced: "x-vendorname:event_name". Receivers must not reject envelopes containing unknown event types.
ai_analysis — AI Insights OPTIONALProducer-generated analysis. Entirely optional and fully extensible. The contents are producer-defined outputs — this specification defines suggested field names but does not mandate how analysis values are computed.
| Field | Type | Description |
|---|---|---|
| purchase_probability | number (0–1) | Estimated probability of purchase. Producer-defined model. |
| recommended_actions | array<string> | Suggested next steps. |
| objections | array<string> | Detected objections. |
| engagement_level | string | "high", "medium", "low" |
| summary | string | Natural language summary for sales teams. |
ACP envelopes are full state snapshots by default. Receivers must replace stored state entirely — no merge logic is required or expected.
Delta envelopes may be generated to represent partial updates between snapshots. A delta envelope contains only acp + events and references a parent snapshot via
parent_envelope_id. When parent_envelope_id is non-null, all other top-level objects must not be present.
This specification does not define when or how frequently producers generate delta envelopes — that is an implementation decision. Producers must send a full snapshot envelope at the conclusion of the interaction, regardless of how many delta envelopes were previously transmitted.
If a delta envelope references a parent_envelope_id that the receiver has not previously ingested, the receiver must reject the envelope with error code UNKNOWN_PARENT_ENVELOPE (HTTP 422). The producer should respond by retransmitting the parent snapshot before retrying the delta.
If deltas arrive out of chronological order (determined by acp.generated_at), receivers should process them in timestamp order. If reordering is not feasible, receivers may reject out-of-order deltas with error code OUT_OF_ORDER_DELTA and request a fresh snapshot.
Producers must not assume delta delivery is guaranteed. The final snapshot at conversation conclusion serves as the authoritative record and must be independently valid without reference to any prior deltas.
ACP uses a two-tier additionalProperties strategy:
| Object | additionalProperties | Rationale |
|---|---|---|
| acp | false | Protocol-controlled. Vendors must not add fields. |
| source, dealership, lead, transaction, conversation | true | Vendors may add namespaced fields. |
| offer_history[], finance_terms, appointment, delivery | false | Protocol-controlled sub-objects. |
| ai_analysis | true | Fully open. Vendors define their own analysis. |
When adding custom fields, vendors should prefix with their namespace to avoid collisions: "x-vendorname:field_name". Receivers must ignore unknown fields without
error.
Producers should validate envelopes against the official JSON Schema before transmission. Receivers must validate on receipt and reject non-conformant envelopes with error code INVALID_SCHEMA.
ACP follows Semantic Versioning 2.0.0. The version string in acp.version consists of MAJOR.MINOR.PATCH.
| Component | Change Type | Compatibility | Example |
|---|---|---|---|
| MAJOR | Breaking structural change | Not backward compatible. Receivers must not assume interoperability between major versions. | 1.0.0 → 2.0.0 |
| MINOR | New optional fields or event types | Backward compatible. Existing v1.0 receivers must continue to function. | 1.0.0 → 1.1.0 |
| PATCH | Clarification, typo fix, documentation | No schema change. Fully compatible. | 1.0.0 → 1.0.1 |
The v1.0.0 schema is frozen upon publication. New optional fields may be introduced in minor versions. Required fields must not be added in minor versions — only in major versions.
Receivers must be forward-compatible within a major version. This means:
Producers targeting a specific version must produce envelopes valid against that version's schema. Producers may include fields from a later minor version, but must not depend on receivers understanding them.
Producers should include the X-ACP-Version HTTP header. Receivers should check acp.version and respond with VERSION_MISMATCH if the major version is unsupported.
A system is ACP-conformant if it meets one or both of the following profiles:
A conformant producer must:
envelope_id values matching pattern ^acp_.events arrays.Content-Type: application/acp+json when transmitting envelopes.A conformant receiver must:
application/acp+json content type.envelope_id, returning success (not error) for duplicates.parent_envelope_id.ACP envelopes may contain PII including names, email addresses, phone numbers, physical addresses, and conversation transcripts. Implementations must handle PII in accordance with applicable regulations (CCPA, GDPR, TCPA, state privacy laws). Producers should document what PII categories their envelopes contain.
ACP envelopes must be transmitted over TLS 1.2 or higher. Unencrypted HTTP transmission is a conformance violation. Producers and receivers should enforce HTTPS-only endpoints.
Receivers must authenticate the source of incoming envelopes. Bearer tokens, mutual TLS, or API key authentication are all acceptable. Receivers should verify that the source.provider value matches the authenticated identity. Unauthorized sources must be rejected with UNAUTHORIZED_SOURCE.
The envelope_id field serves as an idempotency key. Receivers must track processed envelope IDs and reject replayed envelopes. Receivers should also validate
generated_at timestamps — envelopes more than 24 hours old should be flagged for review. This prevents captured envelopes from being re-submitted by malicious actors.
Producers should include an X-ACP-Signature HTTP header containing an HMAC-SHA256 signature of the envelope body. Receivers should verify this signature when present.
A future version of this specification may make envelope signing required.
Producers should include only data relevant to the receiving dealership's legitimate business purpose. Conversation transcripts should not include system prompts, internal reasoning traces, or model configuration details. Producers must not include data from other customers' conversations in an envelope.
Receivers should implement rate limiting to mitigate denial-of-service attacks and resource exhaustion from misconfigured producers. Receivers must respond with RATE_LIMITED (HTTP 429) and include a Retry-After header indicating the number of seconds before the producer may retry.
Producers must respect 429 responses and implement exponential backoff with jitter. A producer that continues to transmit after receiving a 429 response may be permanently blocked by the receiver.
Receivers must use the following standardized error codes in HTTP responses. Error responses must include a JSON body conforming to the canonical error structure:
{
"error": {
"code": "INVALID_SCHEMA",
"message": "Envelope does not conform to ACP v1.0.0 schema",
"details": [
{
"field": "lead.score",
"issue": "Value 150 exceeds maximum of 100"
}
]
}
}
The error.code field must match one of the codes below. The error.message field should be a human-readable explanation. The error.details array is optional and may contain field-level validation errors or contextual information.
| Code | HTTP | Description |
|---|---|---|
| INVALID_SCHEMA | 400 | Envelope failed JSON Schema validation. Response should include validation error details. |
| VERSION_MISMATCH | 400 | The acp.version major version is not supported by this receiver. |
| DUPLICATE_ENVELOPE | 200 | This envelope_id has already been processed. Must return 200, not an error status. |
| ENVELOPE_CONFLICT | 409 | A delta envelope references a parent_envelope_id that does not exist in the receiver's store. |
| UNKNOWN_PARENT_ENVELOPE | 422 | A delta envelope references a parent_envelope_id that the receiver has never ingested. Producer should retransmit the parent snapshot. |
| OUT_OF_ORDER_DELTA | 422 | A delta envelope arrived with a generated_at timestamp earlier than the most recent processed delta. Producer should send a fresh snapshot. |
| PAYLOAD_TOO_LARGE | 413 | Envelope exceeds the receiver's maximum accepted payload size. Producer should truncate conversation.messages and retry. |
| UNAUTHORIZED_SOURCE | 401 | Authentication failed or source.provider does not match authenticated identity. |
| DEALER_NOT_FOUND | 404 | The dealership.id does not map to a known dealer in the receiver's system. |
| RATE_LIMITED | 429 | Too many envelopes. Producers should implement exponential backoff. |
| INTERNAL_ERROR | 500 | Receiver encountered an unexpected error. Producers should retry with backoff. |