diff --git a/docs/topics/failure-handling.md b/docs/topics/failure-handling.md
new file mode 100644
index 00000000..5bb0a183
--- /dev/null
+++ b/docs/topics/failure-handling.md
@@ -0,0 +1,338 @@
+# Handling Failure States in Agent Payment Flows
+
+## Overview
+
+Agent payment flows operate in dynamic environments where conditions can change
+between steps. This document provides guidance on how agents should detect,
+communicate, and recover from common failure scenarios while respecting user
+intent and mandate constraints.
+
+## Guiding Principles
+
+1. **Mandate as Contract**: The mandate represents the user's informed consent.
+ Agents must not exceed mandate constraints without explicit user
+ re-confirmation.
+
+2. **Fail Safely**: When in doubt, pause and confirm with the user rather than
+ proceeding with a potentially unauthorized transaction.
+
+3. **Transparent Communication**: Failure states should be communicated clearly
+ with actionable recovery options.
+
+4. **No Silent Degradation**: Agents should never silently accept worse terms
+ than what the user originally approved.
+
+---
+
+## Failure Scenarios
+
+### 1. Mandate Expiration
+
+**When it occurs:** The `intent_expiry` (IntentMandate) or `cart_expiry`
+(CartMandate) timestamp is reached before the transaction completes.
+
+**Detection:**
+
+```python
+from datetime import datetime, timezone
+
+def is_mandate_expired(expiry_iso: str) -> bool:
+ """Check if a mandate has expired based on its expiry timestamp."""
+ expiry = datetime.fromisoformat(expiry_iso)
+ return datetime.now(timezone.utc) > expiry
+```
+
+**Expected Agent Behavior:**
+
+| Scenario | Action |
+| -------------------------------------------- | ------------------------------------------------ |
+| Intent mandate expired before cart selection | Request new intent mandate from user |
+| Cart mandate expired before payment | Request fresh cart from merchant |
+| Expiry during payment authorization | Abort, request fresh cart + re-confirm with user |
+
+**Example Failure Response:**
+
+```json
+{
+ "category": "MANDATE_EXPIRED",
+ "message": "Cart mandate expired at 2025-01-15T10:30:00Z",
+ "recovery_action": "NEW_MANDATE_REQUIRED",
+ "details": {
+ "mandate_type": "CartMandate",
+ "expired_at": "2025-01-15T10:30:00Z",
+ "cart_id": "cart_abc123"
+ }
+}
+```
+
+**Guidance:**
+
+- **Do**: Inform user: _"Your cart reservation has expired. Would you like me
+ to get a fresh quote?"_
+- **Do**: Preserve user's original intent for easier re-creation
+- **Don't**: Proceed with expired mandate
+
+---
+
+### 2. Price Drift Beyond Mandate Constraints
+
+**When it occurs:** The final price from the merchant exceeds what the user
+originally approved or exceeds explicit price constraints in the mandate.
+
+**Common causes:**
+
+- Dynamic pricing changes between browse and checkout
+- Tax calculation differences
+- Currency fluctuations
+- Surge pricing (travel, delivery)
+
+**Detection:**
+
+```python
+def check_price_drift(
+ original_total: float,
+ current_total: float,
+ tolerance_percent: float = 0.0
+) -> tuple[bool, float]:
+ """
+ Check if price drift is within acceptable tolerance.
+
+ Returns:
+ (is_within_tolerance, drift_percent)
+ """
+ if original_total == 0:
+ return (current_total == 0, 0.0)
+ drift_percent = ((current_total - original_total) / original_total) * 100
+ return (drift_percent <= tolerance_percent, drift_percent)
+```
+
+**Expected Agent Behavior:**
+
+| Drift Amount | Action |
+| ------------------------------- | ------------------------------------------------------ |
+| Within tolerance (if specified) | Proceed with informational note |
+| Exceeds tolerance but < 10% | Pause, confirm with user before proceeding |
+| Exceeds 10% or max_amount | Abort, present new price to user for explicit approval |
+
+**Example Failure Response:**
+
+```json
+{
+ "category": "PRICE_DRIFT",
+ "message": "Final price exceeds original quote by 15%",
+ "recovery_action": "USER_CONFIRMATION_REQUIRED",
+ "details": {
+ "drift_percent": 15.0,
+ "tolerance_percent": 5.0
+ },
+ "original_value": { "amount": 100.0, "currency": "USD" },
+ "actual_value": { "amount": 115.0, "currency": "USD" }
+}
+```
+
+**Guidance:**
+
+- **Do**: _"The price has increased from $100 to $115. Would you like to
+ proceed?"_
+- **Do**: Offer alternatives: _"I found a similar item at [Merchant B] for
+ $98"_
+- **Don't**: Silently pay higher price
+- **Don't**: Round down or hide the difference
+
+---
+
+### 3. Payment Decline / PSP Rejection
+
+**When it occurs:** The payment service provider (PSP) or issuer declines the
+transaction.
+
+**Common decline reasons:**
+
+| Reason Code | Description | User Action Required |
+| -------------------- | ---------------------------- | --------------------------------------- |
+| `INSUFFICIENT_FUNDS` | Account lacks funds | Yes - add funds or use different method |
+| `CARD_EXPIRED` | Payment method expired | Yes - update payment method |
+| `CVV_MISMATCH` | Security code incorrect | Yes - re-enter details |
+| `RISK_REJECTED` | Fraud/risk rules triggered | Maybe - contact issuer |
+| `SCA_REQUIRED` | Strong authentication needed | Yes - complete 3DS/biometric |
+| `LIMIT_EXCEEDED` | Transaction limit hit | Yes - adjust or use different method |
+
+**Expected Agent Behavior:**
+
+```mermaid
+flowchart TD
+ A[Payment Declined] --> B{Is retry allowed
with same method?}
+ B -->|Yes| C[Retry once]
+ B -->|No| D{Alternative
method available?}
+ D -->|Yes| E[Offer to switch
payment method]
+ D -->|No| F[Inform user,
abort gracefully]
+```
+
+**Example Failure Response:**
+
+```json
+{
+ "category": "PAYMENT_DECLINED",
+ "message": "Payment declined: Insufficient funds",
+ "recovery_action": "ALTERNATIVE_PAYMENT_REQUIRED",
+ "decline_code": "INSUFFICIENT_FUNDS",
+ "details": {
+ "retry_with_same_method": false,
+ "alternative_methods_available": true
+ }
+}
+```
+
+**Guidance:**
+
+- **Do**: _"Your payment was declined due to insufficient funds. Would you
+ like to try a different payment method?"_
+- **Do**: If user has multiple saved methods, offer to switch
+- **Don't**: Retry indefinitely
+- **Don't**: Expose sensitive decline details to merchant
+
+---
+
+### 4. Partial Cart Rejection / Out of Stock
+
+**When it occurs:** The merchant cannot fulfill all items in the cart (inventory
+changes, discontinued items, regional restrictions).
+
+**Expected Agent Behavior:**
+
+| Scenario | Action |
+| -------------------------------- | ------------------------------------------- |
+| Some items unavailable | Present modified cart, re-confirm with user |
+| All items unavailable | Inform user, offer alternatives or abort |
+| Price changed on available items | Treat as price drift (see above) |
+
+**Example Failure Response:**
+
+```json
+{
+ "category": "CART_MODIFIED",
+ "message": "2 of 5 items are no longer available",
+ "recovery_action": "USER_CONFIRMATION_REQUIRED",
+ "rejected_items": [
+ {
+ "sku": "SKU-123",
+ "reason": "OUT_OF_STOCK",
+ "message": "Expected restock in 3 days"
+ },
+ {
+ "sku": "SKU-456",
+ "reason": "DISCONTINUED",
+ "alternative_sku": "SKU-789"
+ }
+ ],
+ "original_value": { "item_count": 5, "total": 150.0 },
+ "actual_value": { "item_count": 3, "total": 89.0 }
+}
+```
+
+**Guidance:**
+
+- **Do**: _"2 items are out of stock. The updated cart total is $89.
+ Proceed?"_
+- **Do**: Offer to find alternatives for rejected items
+- **Do**: If total drops significantly, confirm user still wants to proceed
+- **Don't**: Silently remove items and charge
+
+---
+
+## Mandate Enforcement vs. User Re-prompting
+
+### Decision Framework
+
+```mermaid
+flowchart TD
+ A[Condition Changed] --> B{Is change within
mandate constraints?}
+ B -->|Yes| C[Proceed
and log it]
+ B -->|No| D{Is mandate
still valid?}
+ D -->|Yes| E[Re-prompt user
for confirmation]
+ D -->|No| F[Request new
mandate]
+```
+
+### When to Enforce Silently (Proceed)
+
+- Change is explicitly within mandate tolerance
+- Change benefits the user (lower price, faster shipping)
+- Change is purely cosmetic (product image updated)
+
+### When to Re-prompt User
+
+- Price increases beyond tolerance
+- Items modified or substituted
+- Delivery terms changed
+- Any change that could reasonably affect user's decision
+
+### When to Request New Mandate
+
+- Original mandate expired
+- Fundamental constraint violated (wrong merchant, wrong product category)
+- User explicitly revoked mandate
+- Multiple failed attempts to satisfy mandate
+
+---
+
+## Standard Error Response Structure
+
+All agents SHOULD use the `FailureResponse` type from `ap2.types.errors` when
+communicating failures to ensure consistent handling across the ecosystem.
+
+```python
+from ap2.types.errors import (
+ FailureResponse,
+ FailureCategory,
+ RecoveryAction,
+ FAILURE_RESPONSE_DATA_KEY
+)
+
+# Example: Creating a failure response
+failure = FailureResponse(
+ category=FailureCategory.PRICE_DRIFT,
+ message="Price exceeds maximum allowed amount",
+ recovery_action=RecoveryAction.USER_CONFIRMATION_REQUIRED,
+ original_value={"max_amount": 100.00},
+ actual_value={"final_price": 125.00}
+)
+
+# Include in A2A message
+message_builder.add_data(FAILURE_RESPONSE_DATA_KEY, failure.model_dump())
+```
+
+### Recovery Action Reference
+
+| Recovery Action | Meaning | Agent Behavior |
+| ------------------------------ | --------------------- | ------------------------------------ |
+| `PROCEED_WITH_NOTICE` | Change is acceptable | Continue, optionally inform user |
+| `RETRY_ALLOWED` | Transient failure | Retry the operation (with backoff) |
+| `USER_CONFIRMATION_REQUIRED` | Terms changed | Pause and get explicit user approval |
+| `NEW_MANDATE_REQUIRED` | Mandate invalid | Request fresh mandate from user |
+| `ALTERNATIVE_PAYMENT_REQUIRED` | Payment method failed | Offer different payment method |
+| `ABORT_TRANSACTION` | Unrecoverable failure | Terminate gracefully, inform user |
+
+---
+
+## Summary: Agent Behavior Matrix
+
+| Failure Type | Detection Point | Default Action | User Interaction |
+| ---------------------------- | ------------------ | -------------- | --------------------- |
+| Intent expired | Before cart search | Abort | Request new mandate |
+| Cart expired | Before payment | Refresh cart | Confirm new terms |
+| Price drift (in tolerance) | Cart confirmation | Proceed | Informational only |
+| Price drift (over tolerance) | Cart confirmation | Pause | Explicit confirmation |
+| Payment declined (can retry) | Payment response | Retry once | Inform if retry fails |
+| Payment declined (terminal) | Payment response | Abort | Offer alternatives |
+| Partial rejection | Cart confirmation | Pause | Present modified cart |
+| Full rejection | Cart confirmation | Abort | Offer alternatives |
+
+---
+
+## See Also
+
+- [Life of a Transaction](life-of-a-transaction.md) - Normal flow without
+ failures
+- [Core Concepts](core-concepts.md) - Understanding mandates and their
+ constraints
+- [AP2 Specification](../specification.md) - Full protocol specification
diff --git a/mkdocs.yml b/mkdocs.yml
index 72dfc7ec..bff08ed6 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -24,6 +24,7 @@ nav:
- AP2 and x402: topics/ap2-and-x402.md
- Privacy and Security: topics/privacy-and-security.md
- Life of a Transaction: topics/life-of-a-transaction.md
+ - Handling Failure States: topics/failure-handling.md
- AP2 specification: specification.md
- A2A extension for AP2: a2a-extension.md
- Glossary: glossary.md
@@ -143,6 +144,7 @@ plugins:
- topics/ap2-and-x402.md
- topics/privacy-and-security.md
- topics/life-of-a-transaction.md
+ - topics/failure-handling.md
"Specification":
- specification.md
- a2a-extension.md