<!-- Canonical HTML: https://autoagentprotocol.org/docs/v1.3/behavior-rules -->
<!-- Immutable contract: v1.3. -->
# Behavior rules

![Four buyer-agent behavior rules: identify honestly, browse anonymously, respect inventory systems, and quote prices safely](https://autoagentprotocol.org/docs/v1.3/img/agent-etiquette.svg)

This page collects the normative MUST and SHOULD requirements that an AAP-compliant agent must follow. These rules are the bare minimum for interoperability and regulatory compliance; they are referenced from the per-skill pages and applied by the dealer-side test suite.

The keywords MUST, MUST NOT, SHOULD, SHOULD NOT, MAY, RECOMMENDED, and OPTIONAL are interpreted as in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119.html).

## Inventory rules

### Inventory MUSTs

- **Inventory feeds MUST contain only in-stock statuses.** `Vehicle.status` is a controlled enum: `available` | `intransit` | `pending`. Dealer agents MUST only return vehicles whose `status` is one of these three in `inventory.search` results. A vehicle in any other state is OUT OF STOCK: the dealer MUST omit it, and a buyer agent that somehow observes any other value MUST ignore the vehicle and treat it as unavailable. `status` is REQUIRED on every inventory listing.
- **`updated_at` is MANDATORY for availability and pricing claims.** Every `Vehicle` returned by `inventory.search` and `inventory.vehicle` MUST include `updated_at` whenever the agent is making availability or pricing claims about the listing. The field is an RFC 3339 timestamp indicating when the dealer last updated the listing's availability, price, fees, or status.
- **`vehicle.vin` or `vehicle.stock` SHOULD be present on detail responses.** `inventory.vehicle` responses SHOULD include `vin` or `stock`. When neither is present (e.g. a deeply pre-allocated unit), the response MUST include `vehicle_id` and SHOULD include free-text `notes` explaining the unit's identification.
- **When implemented, `inventory.search` MUST support anonymous calls.** A dealer agent is not required to expose `inventory.search`, but if it does, it MUST accept calls without `customer` info and without `consent`. AAP RECOMMENDS dealer agents publish their search surface anonymously by default when they expose one.

### Inventory SHOULDs

- Dealer agents SHOULD update `updated_at` no less frequently than once per business day for each in-stock listing.
- Dealer agents SHOULD echo the request's `pagination.skip` and `pagination.limit` in the response `data.skip` and `data.limit` so buyer agents can paginate without ambiguity.
- Buyer agents SHOULD attach `privacy.anonymous: true` to every `inventory.search` call by default and only attach customer identity when actually submitting a lead.

## Lead rules

![Consent gate separating anonymous inventory access from lead submission with a valid ConsentGrant](https://autoagentprotocol.org/docs/v1.3/img/consent-gate.svg)

### Lead MUSTs

- **`customer` is REQUIRED on every `lead.submit` request.** The unified lead is never anonymous. Every `lead.submit.request` MUST include a `customer` block; the schema enforces this in `required`.
- **`consent` is MANDATORY whenever `customer` is present (which is always for `lead.submit`).** Every `lead.submit.request` MUST include a `consent` (`ConsentGrant`) block. A request that violates this MUST be rejected with `CONTACT_CONSENT_REQUIRED`.
- **Channel must be permitted.** Dealer agents MUST reject the lead with `CONTACT_CONSENT_REQUIRED` if the requested follow-up channel (or the channel implied by `customer.preferred_contact`) is not in `consent.allowed_channels[]`. The dealer MUST NOT use a channel the user did not authorize.
- **Buyer agents MUST NOT include phone or email without explicit user authorization.** A buyer agent MUST capture an explicit consent action from the user — verbatim text shown, channels selected, scope confirmed — before populating `customer.email`, `customer.phone`, or `customer.address`. The verbatim text MUST be reproduced in `consent.consent_text`.
- **Scope is fixed.** `consent.scope[]` MUST be `["lead_submission"]`. A `ConsentGrant` whose `scope` is anything else MUST cause the dealer to reject with `INVALID_CONSENT`.
- **Condition vocabularies MUST NOT be mixed.** `vehicle_of_interest.condition` (when set) MUST be one of `new` | `used` | `cpo`. `trade_in.condition` (when set) MUST be one of `excellent` | `good` | `fair` | `poor`. Mixing these MUST be rejected with `SCHEMA_VALIDATION_FAILED` (the conditional schema enforces it) or `INVALID_CONDITION`.

### Lead SHOULDs

- Dealer agents SHOULD return `status: "duplicate"` (instead of `received`) when the same `customer` submits an equivalent lead within a short window (dealer-defined; commonly 24 hours).
- Dealer agents SHOULD include a brief `message` on every successful lead response explaining the next step (e.g. "A sales rep will email Anna within 1 business day.").
- Dealer agents SHOULD persist the full `ConsentGrant` JSON as an audit record alongside the lead. ADF mapping does not specify a consent element; the `ConsentGrant` is the AAP-side record.
- Buyer agents SHOULD bundle `vehicle_of_interest`, `trade_in`, and `appointment` into a SINGLE `lead.submit.request` when they belong to the same shopping intent, rather than making multiple consecutive submissions. This keeps the dealer CRM transactional and avoids re-stitching one customer intent.

## Appointment rules

### Appointment MUSTs

- **Booking is not implied.** A successful `lead.submit` response does NOT guarantee an appointment booking unless the `data.appointment.status` is `confirmed`. `requested` and `proposed` mean the customer has expressed interest but has not been booked.
- **Non-confirmable requests SHOULD return `requested` or `proposed`.** When the dealer agent cannot auto-confirm a request (manual review required, the requested time does not fit, etc.), it SHOULD respond with `data.appointment.status: "requested"` or `data.appointment.status: "proposed"` rather than rejecting the appointment.
- **`appointment_at` MAY be omitted.** When the buyer does not specify a time, the dealer SHOULD return `requested` and follow up to schedule. When present, `appointment_at` MUST be an ISO 8601 / RFC 3339 timestamp with a timezone offset.
- **`vehicle_of_interest` SHOULD be present for `test_drive`.** When `appointment.appointment_type` is `test_drive`, the lead SHOULD also include `vehicle_of_interest` so the dealer knows which unit. A submission without `vehicle_of_interest` MAY be rejected with `MISSING_REQUIRED_FIELD` per dealer policy.

### Appointment SHOULDs

- The dealer agent SHOULD include the dealer's primary phone (`data.dealer.phone`, E.164) on every lead-with-appointment response so the buyer agent can surface it to the user.
- The dealer agent SHOULD include `data.appointment.confirmed_at` on every `confirmed` appointment status and `data.appointment.proposed_times` on every `proposed` status.
- Buyer agents that receive `data.appointment.status: "proposed"` SHOULD present the alternative times to the user and re-submit a fresh `lead.submit` with the chosen time in `appointment.appointment_at`.

## Defaults and locale rules

When optional context fields are omitted, AAP defines deterministic fallbacks so buyer agents and dealer agents agree without out-of-band coordination.

- **Prices and fee amounts are integers in whole US dollars.** `msrp`, `list_price`, `price`, and `fees[].amount` use plain integers (e.g. `26780` or `500`).
- **Address default country.** `Address.country` is optional; when omitted, the assumed country is `US`.
- **Idempotency.** Buyer agents that retry `lead.submit` after a network failure SHOULD pass an `idempotency_key` (UUID recommended). Dealer agents SHOULD dedupe on this key for at least 24 hours and return the original `lead_id` and status on retries.
- **Consent expiration.** When `ConsentGrant.expires_at` is omitted, the dealer MAY apply its own default expiration window per local regulation; an explicit `expires_at` always wins. The dealer MUST reject the lead with `INVALID_CONSENT` if the grant has already expired.

## Pricing rules

### Pricing MUSTs

- **`price` MUST be the authoritative advertised vehicle price.** It includes all mandatory, non-government dealer charges and required dealer add-ons, may use only discounts available to every consumer, and is not reduced by an additional required down payment or conditioned on dealer financing. It excludes government charges such as tax, title, and registration, so it is not an out-the-door quote.
- **`fees` is an optional breakdown, not a condition of `price`.** `Vehicle.price` remains authoritative when `Vehicle.fees` is omitted. When `fees` is present, `[]` means no mandatory dealer charges and a non-empty array completely itemizes charges already included in `price`. Buyer agents MUST NOT add those amounts again or join them from `dealer.information`.
- **`price_min` / `price_max` and `sort.field: "price"` apply to `price`.** `inventory.search` `filters.price_min`, `filters.price_max`, and `sort.field: "price"` are evaluated against `price`, not `list_price` or `msrp`. Vehicles without `price` do not match price-bound filters. See [Pricing and fee disclosure](https://autoagentprotocol.org/docs/v1.3/pricing-and-ftc.md).

### Pricing SHOULDs

- Dealer agents SHOULD publish `list_price`, `msrp`, `price`, and `fees` together when all are known. `list_price + sum(fees)` need not equal `price` because `price` may include universally available discounts; buyer agents do not derive a replacement price.
- Buyer agents SHOULD compare offers across dealers on `price`, not `list_price`. If only `list_price` and optional `fees` are known, the publisher omits `price`, and consumer agents treat those components as informational rather than payable or computable.

## Authentication

AAP v1.3.0 defines **no authentication of its own** — agents are public by default. A dealer that needs to protect its endpoint uses A2A's native `securitySchemes` / `securityRequirements` on its agent card; obtaining and presenting credentials then follows A2A (a transport concern), not AAP. See [Discovery](https://autoagentprotocol.org/docs/v1.3/discovery.md#authentication).

## Rate limits

- Dealer agents SHOULD return `RATE_LIMITED` (HTTP 429 / JSON-RPC code -32002) with `retryable: true` and a hint in `details.retry_after_ms` when the buyer agent exceeds a per-key quota.

## Ordering of rules

When two rules appear to conflict, the more restrictive one wins. For example: a vehicle whose `status` is not one of `available` | `intransit` | `pending` is out of stock and MUST NOT be returned as available, even if the dealer's own internal cache says otherwise. The buyer agent's choice of `privacy.anonymous: true` does NOT override the consent rules — those apply only to lead.* skills, where customer info changes the call.
