# RevCent MCP Guide: `UpdatePendingSale`

AI/MCP-focused guide for the RevCent `UpdatePendingSale` operation.

This document is meant to be read by AI agents, MCP clients, automation tools, and developers that need to understand how to modify an existing pending Sale in RevCent before payment is processed.

---

## Operation Summary

`UpdatePendingSale` updates an existing pending Sale using the `sale_id` returned by `CreatePendingSale`.

A pending Sale is stored in RevCent but has **not yet processed payment**. It acts like a pseudo-cart or stored checkout session for a specific customer. `UpdatePendingSale` lets the AI/MCP client modify that stored cart/session before the Sale is officially processed.

The MCP operation description states that `UpdatePendingSale`:

- Updates an existing pending Sale using the Sale ID of a previously created pending Sale.
- Does **not** process payment.
- Can add or modify attributes such as payment details, addresses, products, and more.
- Uses `pending_options` for specific behavior when adding or modifying products, shipping, tax, or discounts.

Use `ProcessPendingSale` later when the pending Sale is ready to be charged.

---

## Related Documentation Links

AI/MCP clients should use these related markdown documents for broader Sale lifecycle and pending Sale workflow context:

| Document | URL | Purpose |
|---|---|---|
| Sales Overview | `https://revcent.com/documentation/markdown/mcp/operation/OverviewSale.md` | Explains the overall Sale object, Sale lifecycle, Product Sales, shipments, tax, discounts, refunds, subscriptions, trials, fulfillment, events, AI workflows, and reporting rules. |
| Estimate Sale | `https://revcent.com/documentation/markdown/mcp/operation/EstimateSale.md` | Explains how to estimate Sale totals, products, shipping, tax, coupons, and discounts before payment is processed. Useful after updating a pending Sale and before processing payment. |
| Process Pending Sale | `https://revcent.com/documentation/markdown/mcp/operation/ProcessPendingSale.md` | Explains how to process payment for a pending Sale when the customer/cart/funnel is ready to be charged. Use after `CreatePendingSale` and any needed `UpdatePendingSale` calls. |

Recommended pending Sale reading order:

```text
OverviewSale.md
    ↓
CreatePendingSale.md
    ↓
UpdatePendingSale.md
    ↓
EstimateSale.md
    ↓
ProcessPendingSale.md
```

---

## Why Use `UpdatePendingSale`?

Use `UpdatePendingSale` when a pending Sale already exists and something about the stored cart, customer, payment, shipping, tax, or discount context needs to change before payment.

Common reasons:

| Reason | Example |
|---|---|
| Add an upsell | Customer accepts a post-checkout upsell before final charge. |
| Add an order bump | Customer adds a small add-on product before payment. |
| Add a downsell | Customer rejects an upsell but accepts a lower-priced offer. |
| Change product quantity | Customer increases quantity before final payment. |
| Replace cart contents | Customer changes their cart entirely before payment. |
| Add payment details | Customer enters credit card information after the initial pending Sale was created. |
| Update card details | Customer provides a different card before payment. |
| Update billing address | Billing details change before processing. |
| Update shipping address | Shipping destination changes before processing. |
| Add or replace shipping | Customer chooses a shipping method. |
| Add or replace tax | Tax is recalculated before checkout. |
| Add or replace discounts | Funnel discount, coupon result, or support credit changes before payment. |
| Add tracking metadata | Attach visitor/session, affiliate, funnel, or campaign metadata. |
| Recover abandoned checkout | AI/email/voice workflow updates the pending Sale before processing. |

`UpdatePendingSale` is especially useful for ecommerce funnels where the customer journey changes before payment is finally submitted.

---

## `UpdatePendingSale` Does Not Process Payment

`UpdatePendingSale` never charges the customer.

It modifies a pending Sale only.

```text
UpdatePendingSale = modify pending Sale
ProcessPendingSale = process payment
```

Do not tell the user or customer that payment has been processed after `UpdatePendingSale`.

---

## Pending Sale Workflow

Typical flow:

```text
CreatePendingSale
    ↓
UpdatePendingSale
    ↓
UpdatePendingSale again if needed
    ↓
EstimateSale
    ↓
ProcessPendingSale
```

`UpdatePendingSale` can be used multiple times before payment is processed.

This supports:

- Multi-step checkout
- Upsell flows
- Order bumps
- Downsell flows
- Abandoned checkout recovery
- AI-assisted recovery
- AI Voice Agent live recovery
- Support-assisted checkout
- Custom checkout/session logic

---

## Schema Summary

Operation:

```text
name: UpdatePendingSale
title: Update A Pending Sale
description: Update an existing pending Sale without processing payment.
input type: object
additionalProperties: false
```

Required top-level fields:

```text
campaign
sale_id
```

The input schema does **not** allow unknown top-level fields.

---

## Required Fields

| Field | Type | Required | Description |
|---|---:|---:|---|
| `campaign` | string | Yes | 20-character Campaign ID. |
| `sale_id` | string | Yes | 20-character Sale ID of the pending Sale to update. |

`campaign` is required even though the pending Sale already exists.

`sale_id` must be the Sale ID returned by `CreatePendingSale` or retrieved from the pending Sale workflow.

---

## Full Top-Level Input Schema

| Field | Type | Required | Description |
|---|---:|---:|---|
| `campaign` | string | Yes | 20-character Campaign ID. |
| `sale_id` | string | Yes | 20-character Sale ID. |
| `iso_currency` | string | No | Three-character ISO 4217 currency code. |
| `ip_address` | string | No | Customer IP address. |
| `credit_card` | object | No | Credit card payment information to add/update on the pending Sale for later processing. |
| `customer` | object | No | Customer details to add/update. |
| `bill_to` | object | No | Billing information. |
| `ship_to` | object | No | Shipping destination. |
| `product` | array<object> | No | Products to add/modify according to `pending_options.exists_options.product`. |
| `shipping` | array<object> | No | Shipping entries to add/modify according to `pending_options.exists_options.shipping`. |
| `tax` | array<object> | No | Tax entries to add/modify according to `pending_options.exists_options.tax`. |
| `discount` | array<object> | No | Discount entries to add/modify according to `pending_options.exists_options.discount`. |
| `internal_sale_id` | string | No | Optional internal Sale ID, such as a third-party order ID. |
| `internal_customer_id` | string | No | Optional internal Customer ID, such as a third-party customer ID. |
| `metadata` | array<object> | No | Sale-level metadata passed down to all items created as a result of the Sale, including customer, product sales, shipping, and tax. |
| `pending_options` | object | No | Controls update behavior for existing products, shipping, tax, and discounts. |

---

## Output Schema

Successful output can include:

| Field | Type | Description |
|---|---:|---|
| `api_call_id` | string | 20-character API call ID. |
| `api_call_unix` | integer | Unix timestamp when the API call was initiated. |
| `code` | integer | API response code. `1` indicates success. |
| `campaign_id` | string | 20-character Campaign ID. |
| `customer_id` | string | 20-character Customer ID. |
| `sale_id` | string | 20-character Sale ID of the updated pending Sale. |
| `result` | string | Result message. |

---

## Most Important Concept: `pending_options`

`pending_options` controls how submitted items interact with items that already exist in the pending Sale.

The schema states:

```text
When updating a pending sale you have options available that allow you to specify what actions occur given specific conditions.
```

Use `pending_options.exists_options` when updating:

- Products
- Shipping
- Tax
- Discounts

If `pending_options` is omitted, defaults apply. For many item types the default is `replace`, which can remove existing items and replace them with the submitted items.

AI/MCP clients must be careful with defaults.

---

## `pending_options` Schema

```json
{
  "pending_options": {
    "exists_options": {
      "product": "replace | skip | merge_replace | merge_skip | merge_combine",
      "shipping": "replace | skip",
      "tax": "replace | skip",
      "discount": "replace | skip"
    }
  }
}
```

| Field | Type | Description |
|---|---:|---|
| `pending_options.exists_options.product` | string enum | Controls what happens when submitted products interact with existing products in the pending Sale. |
| `pending_options.exists_options.shipping` | string enum | Controls what happens if shipping already exists in the pending Sale. |
| `pending_options.exists_options.tax` | string enum | Controls what happens if tax already exists in the pending Sale. |
| `pending_options.exists_options.discount` | string enum | Controls what happens if discounts already exist in the pending Sale. |

---

## Product Update Options

Product update behavior is the most important part of `UpdatePendingSale`.

RevCent matches products based on the product `id` submitted in the original request and update request.

Allowed values:

```json
[
  "replace",
  "skip",
  "merge_replace",
  "merge_skip",
  "merge_combine"
]
```

### Product Option: `replace`

```json
{
  "pending_options": {
    "exists_options": {
      "product": "replace"
    }
  }
}
```

Behavior:

- Removes all existing products in the pending Sale.
- Replaces them with only the products submitted in the update request.
- This is the default.

Use when:

- The customer changed the cart entirely.
- The update request represents the complete new cart.
- You intentionally want only the submitted products to remain.

Risk:

- Existing products not included in the update request are removed.

### Product Option: `skip`

```json
{
  "pending_options": {
    "exists_options": {
      "product": "skip"
    }
  }
}
```

Behavior:

- Leaves existing products in the pending Sale.
- Does not add submitted products.
- Ignores product updates.

Use when:

- You are updating payment, customer, shipping, tax, discount, or metadata and do not want product changes.
- Product array is accidentally present but should not modify cart contents.

### Product Option: `merge_replace`

```json
{
  "pending_options": {
    "exists_options": {
      "product": "merge_replace"
    }
  }
}
```

Behavior:

- Adds new products not already in the pending Sale.
- Keeps existing products that are not submitted.
- Replaces existing products with the same product ID using the submitted version.

Use when:

- You want to add new products and update same-ID product quantity/price/metadata.
- You want to keep all other existing cart products.
- You are updating an existing upsell line item.

Example:

```text
Existing: Product A qty 1, Product B qty 1
Submitted: Product B qty 3, Product C qty 1
Result: Product A qty 1, Product B qty 3, Product C qty 1
```

### Product Option: `merge_skip`

```json
{
  "pending_options": {
    "exists_options": {
      "product": "merge_skip"
    }
  }
}
```

Behavior:

- Adds new products not already in the pending Sale.
- Does not remove or modify existing products.
- If submitted product ID already exists, the existing product remains unchanged.

Use when:

- You only want to add new products.
- You want to add an upsell or order bump without touching existing cart products.
- You do not want to overwrite an existing same-ID line item.

Example:

```text
Existing: Product A qty 1, Product B qty 1
Submitted: Product B qty 3, Product C qty 1
Result: Product A qty 1, Product B qty 1, Product C qty 1
```

### Product Option: `merge_combine`

```json
{
  "pending_options": {
    "exists_options": {
      "product": "merge_combine"
    }
  }
}
```

Behavior:

- Adds new products not already in the pending Sale.
- Keeps existing products.
- For submitted products that already exist, combines quantities.

Use when:

- You want submitted quantity to increment existing quantity.
- Customer adds another unit of a product already in the cart.
- You are treating submitted products like “add to cart” actions.

Example:

```text
Existing: Product A qty 1, Product B qty 1
Submitted: Product B qty 3, Product C qty 1
Result: Product A qty 1, Product B qty 4, Product C qty 1
```

---

## Product Update Decision Guide

| Intent | Recommended `product` Option |
|---|---|
| Replace entire cart | `replace` |
| Update non-product fields only | `skip` |
| Add new products and update same-ID products | `merge_replace` |
| Add upsell/order bump without touching existing products | `merge_skip` |
| Add quantities to existing same-ID products | `merge_combine` |

Best practice:

- Use `merge_skip` for adding an upsell or order bump.
- Use `merge_combine` when the customer explicitly adds more quantity of an existing product.
- Use `merge_replace` when the same product should be updated with a new quantity, price, or metadata.
- Use `replace` only when the submitted `product` array represents the complete intended cart.

---

## Shipping Update Options

Allowed values:

```json
[
  "replace",
  "skip"
]
```

### Shipping Option: `replace`

Default behavior.

- Replaces existing shipping with submitted shipping.
- Use when the customer chooses a new shipping method or shipping is recalculated.

### Shipping Option: `skip`

- Skips submitted shipping if shipping already exists.
- Use when you do not want to modify existing shipping.

Example:

```json
{
  "pending_options": {
    "exists_options": {
      "shipping": "replace"
    }
  }
}
```

---

## Tax Update Options

Allowed values:

```json
[
  "replace",
  "skip"
]
```

### Tax Option: `replace`

Default behavior.

- Replaces existing tax with submitted tax.
- Use when tax is recalculated due to address, product, shipping, or discount changes.

### Tax Option: `skip`

- Skips submitted tax if tax already exists.
- Use when you do not want to modify existing tax.

Example:

```json
{
  "pending_options": {
    "exists_options": {
      "tax": "replace"
    }
  }
}
```

---

## Discount Update Options

Allowed values:

```json
[
  "replace",
  "skip"
]
```

### Discount Option: `replace`

Default behavior.

- Replaces existing discounts with submitted discounts.
- Use when applying a new funnel discount or customer service discount.

### Discount Option: `skip`

- Skips submitted discounts if discounts already exist.
- Use when preserving existing discounts.

Example:

```json
{
  "pending_options": {
    "exists_options": {
      "discount": "replace"
    }
  }
}
```

---

## Customer and Address Updates

Use `customer`, `bill_to`, and `ship_to` to update customer and address-related details before payment.

These are useful when:

- Customer enters missing checkout information.
- Shipping address is corrected.
- Billing address differs from customer profile.
- AI Voice Agent or support rep collects updated contact details.
- A custom checkout flow gradually collects customer details.

---

## `customer` Object Schema

`customer` has:

```text
additionalProperties: false
required when object is provided:
  - first_name
  - last_name
  - email
```

| Field | Type | Required | Description |
|---|---:|---:|---|
| `first_name` | string | Yes | Customer first name. |
| `last_name` | string | Yes | Customer last name. |
| `email` | string/email | Yes | Customer email. |
| `address_line_1` | string | No | Customer first address line. |
| `address_line_2` | string | No | Customer second address line. |
| `city` | string | No | Customer city. |
| `state` | string | No | Customer state. |
| `zip` | string | No | Customer ZIP/postal code. |
| `country` | string | No | Customer country in three-letter ISO 3166-1 alpha-3 format. |
| `company` | string | No | Customer company. |
| `phone` | string | No | Customer phone. |

---

## `bill_to` Object Schema

`bill_to` has:

```text
additionalProperties: false
required when object is provided:
  - first_name
  - last_name
  - email
```

Use it when billing details differ from customer details.

---

## `ship_to` Object Schema

`ship_to` has:

```text
additionalProperties: false
required when object is provided:
  - first_name
  - last_name
  - email
```

Use it when shipping details differ from customer or billing details.

---

## Credit Card Updates

Use `credit_card` to add or update payment information on the pending Sale.

This will be used later when the pending Sale is processed for payment.

`UpdatePendingSale` still does **not** process payment.

### `credit_card` Schema

`credit_card` has:

```text
additionalProperties: false
required:
  - card_number
  - exp_month
  - exp_year
```

| Field | Type | Required | Description |
|---|---:|---:|---|
| `card_number` | string | Yes | Full credit card number. |
| `exp_month` | integer | Yes | Expiration month as one or two digit integer. |
| `exp_year` | integer | Yes | Expiration year as two digit integer. |
| `card_code` | string | No | Credit card code. |
| `three_ds` | object | No | Optional 3DS values. |

Credit card data is sensitive. AI/MCP clients should only pass card information through secure, approved payment collection workflows.

---

## Product Array Schema

Each submitted product object has:

```text
additionalProperties: false
required:
  - id
```

| Field | Type | Required | Description |
|---|---:|---:|---|
| `id` | string | Yes | 20-character Product ID. |
| `quantity` | integer | No | Quantity being purchased. Default is 1. |
| `price` | number | No | Price override if different from product default price. |
| `metadata` | array<object> | No | Product-level metadata passed to the eventual Product Sale. |

Product-level metadata is extremely useful for reporting, especially in funnels.

Examples:

```json
{
  "name": "is_upsell",
  "value": "true"
}
```

```json
{
  "name": "funnel_step",
  "value": "order_bump"
}
```

---

## Shipping Array Schema

Each submitted shipping object has:

```text
additionalProperties: false
required:
  - amount
```

| Field | Type | Required | Description |
|---|---:|---:|---|
| `amount` | number | Yes | Amount to charge for the shipping entry. Can be 0. |
| `provider` | string | No | Shipping provider. |
| `provider_method` | string | No | Shipping provider method. |
| `name` | string | No | Optional shipping name. |
| `description` | string | No | Optional shipping description. |
| `metadata` | array<object> | No | Shipping-level metadata passed to the eventual shipping item. |

---

## Tax Array Schema

Each submitted tax object has:

```text
additionalProperties: false
required:
  - amount
```

| Field | Type | Required | Description |
|---|---:|---:|---|
| `amount` | number | Yes | Amount to charge for the tax entry. |
| `name` | string | No | Optional tax name. |
| `description` | string | No | Optional tax description. |
| `metadata` | array<object> | No | Tax-level metadata passed to the eventual tax item. |

---

## Discount Array Schema

Each submitted discount object has:

```text
additionalProperties: false
required:
  - discount_value
  - discount_type
```

| Field | Type | Required | Description |
|---|---:|---:|---|
| `discount_value` | number | Yes | Discount value relative to `discount_type`. |
| `discount_type` | string enum | Yes | Either `percent` or `amount`. |
| `name` | string | No | Optional discount name. |
| `description` | string | No | Optional discount description. |
| `apply_to_product` | boolean | No | Whether discount applies to products. Default is true. |
| `apply_to_shipping` | boolean | No | Whether discount applies to shipping. Default is true. |

Allowed discount types:

```json
[
  "percent",
  "amount"
]
```

---

## Metadata Schema

Metadata can be used at several levels:

- Sale-level `metadata`
- Product-level `product[].metadata`
- Shipping-level `shipping[].metadata`
- Tax-level `tax[].metadata`

Each metadata object has:

```text
additionalProperties: false
required:
  - name
  - value
```

| Field | Type | Required | Limit |
|---|---:|---:|---|
| `name` | string | Yes | 1 to 100 characters. |
| `value` | string | Yes | 1 to 255 characters. |

Sale-level metadata is passed down to all items created as a result of the Sale, including customer, product sales, shipping, and tax.

---

## Visitor Tracking Metadata

If the Sale took place on a website and tracking-domain DNS is set up in RevCent for the website, the Sale-level metadata must include visitor/session cookie values:

```json
[
  {
    "name": "revcent_track_id",
    "value": "[the value of visitor/session cookie.revcent_track_id]"
  },
  {
    "name": "revcent_entry_id",
    "value": "[the value of visitor cookie.revcent_entry_id]"
  }
]
```

When updating a pending Sale, preserve or add these values if they were not included originally.

These values help RevCent associate the pending Sale and eventual processed Sale with tracking/domain attribution.

---

## Offline Payment Third-Party Metadata

For offline payment Sales involving a third-party payment processor, Sale-level metadata must include:

```json
[
  {
    "name": "offline_payment_third_party",
    "value": "sezzle | afterpay | klarna | affirm | amazon_pay"
  },
  {
    "name": "offline_payment_third_party_transaction_id",
    "value": "[the transaction ID from the third party]"
  },
  {
    "name": "offline_payment_third_party_integration_id",
    "value": "[the RevCent ID of the third party integration within your account for the third party processor]"
  }
]
```

Preserve or add these when updating a pending Sale that uses an offline third-party payment processor.

---

## Example: Add an Upsell Without Touching Existing Products

Use `merge_skip` to add a new upsell product while leaving all existing products unchanged.

```json
{
  "campaign": "XXXXXXXXXXXXXXXXXXXX",
  "sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "product": [
    {
      "id": "UPSELL_PRODUCT_IDXX",
      "quantity": 1,
      "metadata": [
        {
          "name": "is_upsell",
          "value": "true"
        },
        {
          "name": "funnel_step",
          "value": "upsell_1"
        }
      ]
    }
  ],
  "pending_options": {
    "exists_options": {
      "product": "merge_skip"
    }
  }
}
```

---

## Example: Replace the Entire Cart

Use `replace` when the submitted products are the complete intended cart.

```json
{
  "campaign": "XXXXXXXXXXXXXXXXXXXX",
  "sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "product": [
    {
      "id": "NEW_PRODUCT_IDXXXXX",
      "quantity": 1
    }
  ],
  "pending_options": {
    "exists_options": {
      "product": "replace"
    }
  }
}
```

Warning: existing products not submitted will be removed.

---

## Example: Update Quantity or Metadata for an Existing Product

Use `merge_replace` to update an existing same-ID product while keeping other existing products.

```json
{
  "campaign": "XXXXXXXXXXXXXXXXXXXX",
  "sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "product": [
    {
      "id": "EXISTING_PRODUCT_ID",
      "quantity": 3,
      "metadata": [
        {
          "name": "quantity_update_reason",
          "value": "customer_requested_more"
        }
      ]
    }
  ],
  "pending_options": {
    "exists_options": {
      "product": "merge_replace"
    }
  }
}
```

---

## Example: Increment Quantity of Existing Product

Use `merge_combine` when the submitted quantity should be added to the existing quantity.

```json
{
  "campaign": "XXXXXXXXXXXXXXXXXXXX",
  "sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "product": [
    {
      "id": "EXISTING_PRODUCT_ID",
      "quantity": 2
    }
  ],
  "pending_options": {
    "exists_options": {
      "product": "merge_combine"
    }
  }
}
```

---

## Example: Update Payment Details Only

Use `product: skip` if a product array is included but products should not change. If no product update is needed, omit the `product` field entirely.

```json
{
  "campaign": "XXXXXXXXXXXXXXXXXXXX",
  "sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "credit_card": {
    "card_number": "<CARD_NUMBER_COLLECTED_SECURELY>",
    "exp_month": 12,
    "exp_year": 29,
    "card_code": "<CARD_CODE_COLLECTED_SECURELY>"
  },
  "pending_options": {
    "exists_options": {
      "product": "skip",
      "shipping": "skip",
      "tax": "skip",
      "discount": "skip"
    }
  }
}
```

---

## Example: Update Shipping and Recalculate Tax

```json
{
  "campaign": "XXXXXXXXXXXXXXXXXXXX",
  "sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "ship_to": {
    "first_name": "Jane",
    "last_name": "Customer",
    "email": "jane@example.com",
    "address_line_1": "789 Shipping Rd",
    "city": "Brooklyn",
    "state": "NY",
    "zip": "11201",
    "country": "USA"
  },
  "shipping": [
    {
      "amount": 6.95,
      "provider": "usps",
      "provider_method": "priority",
      "name": "USPS Priority"
    }
  ],
  "tax": [
    {
      "amount": 3.25,
      "name": "Sales Tax"
    }
  ],
  "pending_options": {
    "exists_options": {
      "shipping": "replace",
      "tax": "replace"
    }
  }
}
```

---

## Example: Apply a New Funnel Discount

```json
{
  "campaign": "XXXXXXXXXXXXXXXXXXXX",
  "sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "discount": [
    {
      "discount_value": 10,
      "discount_type": "percent",
      "name": "Upsell Funnel Discount",
      "apply_to_product": true,
      "apply_to_shipping": false
    }
  ],
  "pending_options": {
    "exists_options": {
      "discount": "replace"
    }
  }
}
```

---

## AI/MCP Workflow Guidance

### Multi-Step Checkout

Use `UpdatePendingSale` after each significant customer action:

1. Customer enters customer details.
2. `CreatePendingSale` creates the pending Sale.
3. Customer selects shipping.
4. `UpdatePendingSale` updates shipping and tax.
5. Customer accepts order bump.
6. `UpdatePendingSale` adds order bump product with `merge_skip`.
7. Customer accepts upsell.
8. `UpdatePendingSale` adds upsell product with `merge_skip`.
9. `EstimateSale` previews final total.
10. `ProcessPendingSale` processes payment.

### Upsell / Order Bump

For most upsell and order bump additions, use:

```json
{
  "product": "merge_skip"
}
```

This adds new products without touching existing products.

Use product-level metadata to make reporting granular.

### Abandoned Checkout Recovery

AI Assistants, AI Voice Agents, or support workflows can update a pending Sale before processing payment.

Examples:

- Add a recovery discount.
- Update card information.
- Update shipping address.
- Add/remove product based on customer preference.
- Add metadata indicating recovery outcome.
- Estimate totals before processing payment.

### AI Voice Agent Recovery

An AI Voice Agent can update a pending Sale live with the customer, but should only process payment through `ProcessPendingSale` when the customer clearly consents and the agent is configured with proper system actions.

---

## Reporting and Metrics

Do not use operational listing endpoints for reporting.

Use `BigQueryRunQuery` for:

- Pending Sale update frequency
- Pending-to-paid conversion rate
- Abandoned checkout recovery
- Upsell conversion
- Order bump conversion
- Funnel step revenue
- Product metadata reporting
- Shipping/tax/discount reporting
- AI recovery outcomes
- AI Voice Agent recovery outcomes
- Refunds by funnel step
- DNS tracking attribution
- Customer lifetime value by funnel

Product-level metadata is especially useful for reports because it passes to the eventual Product Sale.

Sale-level metadata is useful because it passes down to customer, product sales, shipping, and tax.

---

## Validation Checklist Before Calling `UpdatePendingSale`

Before calling `UpdatePendingSale`, verify:

- `sale_id` is the correct pending Sale ID.
- `campaign` is the correct Campaign ID.
- The pending Sale has not already been processed, voided, or otherwise made ineligible for update.
- The requested update should not process payment yet.
- The correct `pending_options.exists_options` values are selected.
- Product updates will not accidentally remove existing products.
- Product-level metadata is included for upsells, order bumps, downsells, or funnel reporting.
- Shipping/tax/discount updates are intentional.
- Credit card details, if provided, are collected securely.
- Visitor tracking metadata is preserved when applicable.
- Offline third-party metadata is preserved when applicable.
- `EstimateSale` will be used if totals need to be shown before payment.
- `ProcessPendingSale` will be used only when the customer/cart/funnel is ready to charge.

---

## Common Mistakes to Avoid

Do not:

- Tell the user payment was processed after `UpdatePendingSale`.
- Forget that the default product behavior is `replace`.
- Use `replace` when the goal is to add an upsell.
- Use `merge_combine` when the goal is to replace quantity.
- Use `merge_skip` when the goal is to update an existing same-ID product.
- Accidentally remove existing products by submitting an incomplete product array with `replace`.
- Omit product-level metadata for upsells/order bumps.
- Omit required `campaign` or `sale_id`.
- Guess the pending Sale ID.
- Lose tracking metadata during updates.
- Omit offline third-party metadata when applicable.
- Use `UpdatePendingSale` when the user wants to charge immediately; use `ProcessPendingSale` when ready to process payment.
- Use operational endpoints for reports instead of `BigQueryRunQuery`.

---

## Final AI/MCP Instruction

Use `UpdatePendingSale` to modify a pending Sale before payment is processed.

The most important implementation detail is `pending_options.exists_options`. It determines whether submitted products, shipping, tax, and discounts replace, skip, merge, or combine with existing pending Sale items.

For ecommerce funnels, use `merge_skip` to add upsells or order bumps without touching existing products, use product-level metadata for granular reporting, use `EstimateSale` to preview totals, and use `ProcessPendingSale` only when payment should actually be processed.


---
Document Parent Directory
* [Operations](https://revcent.com/documentation/markdown/mcp/operation/index.md) - AI/MCP details and overviews for operations available within the RevCent MCP.