# RevCent ProductSales Operations Overview for AI/MCP Clients

## Purpose

This document is an AI/MCP-focused overview of the RevCent ProductSales-related operations:

- `GetProductSales`
- `GetProductSale`
- `RefundProductSale`

Product Sales are the individual product line items within a sale. A single sale can contain one or more products, and each purchased product line is represented as its own Product Sale item. In RevCent, this line-item structure is important because it makes product-level revenue, refund, discount, shop, campaign, and customer analysis much more granular than only looking at the parent Sale.

The Product Sale item is one of the core reasons RevCent is strong for business analysis: it gives AI/MCP clients a product-level financial object instead of forcing all reporting to happen only at the order/sale level.

---

## Mental Model

### Sale vs Product Sale

A `sale` is the parent purchase event or order.

A `product_sale` is an individual product line item within that sale.

For example, if a customer purchases:

- Product A for $30
- Product B for $20
- Product C for $10

RevCent may have one Sale with three related Product Sale items. Each Product Sale can carry its own product, amount, refund, discount, campaign, customer, shop, metadata, and related entity context.

Use this mental model:

```text
Sale
├── Product Sale: Product A line item
├── Product Sale: Product B line item
├── Product Sale: Product C line item
├── Shipping item(s)
├── Tax item(s)
├── Discount item(s)
├── Transaction(s)
└── Other related entities
```

When an AI/MCP client retrieves a Sale with `GetSale`, the sale contains a `product_sales` array with the IDs of each individual product purchased. Each ID can be retrieved with `GetProductSale`.

---

## Why Product Sales Matter

Product Sales provide a line-item-level source of truth for product performance. They are useful for:

- Measuring product-level gross revenue.
- Measuring product-level net revenue.
- Measuring product-level refunds.
- Measuring refund rates by product.
- Measuring performance by campaign, shop, payment type, or customer.
- Understanding which products are sold together in multi-product sales.
- Separating product revenue from shipping, tax, discounts, and transaction-level context.
- Running more accurate business analysis through `BigQueryRunQuery`.

When the user asks product-level business questions, an AI/MCP client should strongly consider Product Sales as the analytical layer instead of only using parent Sale records.

---

## Important Analytics Rule

Do **not** use `GetProductSales` for counting, aggregations, metrics, data mining, bulk retrieval, or document-property searching.

For analytics and reporting, use `BigQueryRunQuery`.

`GetProductSales` is best for paginated operational retrieval and inspection. `BigQueryRunQuery` is best for metrics and analysis.

### Use `GetProductSales` when:

- The user needs to browse or inspect recent Product Sale records.
- The user needs a small paginated list.
- The user is trying to identify a specific Product Sale ID.
- The user needs records filtered by date, campaign, currency, shop, payment type, product, metadata, or customer.

### Use `GetProductSale` when:

- The user has a Product Sale ID and needs the full details.
- The user is about to refund a specific Product Sale.
- The user wants to inspect the line item, product details, amount fields, customer, shop, metadata, or related entity IDs.

### Use `BigQueryRunQuery` when:

- The user asks for metrics.
- The user asks for totals, counts, sums, averages, rankings, or trends.
- The user asks for product-level revenue, refunds, refund rates, or business intelligence.
- The user asks for analysis across many Product Sale records.
- The user asks “how much,” “how many,” “top products,” “refund rate,” “performance,” “compare,” “trend,” or similar analytical requests.

Before using `BigQueryRunQuery`, the AI/MCP client should retrieve BigQuery table schemas with `GetBigQueryTables` and avoid guessing table or column names.

---

## Product-Level Refund Best Practices

### Best practice for partial refunds on initial sales

For initial sale refunds where only part of the sale is being refunded, it is best practice to issue the refund at the Product Sale level using `RefundProductSale`.

This preserves product-level refund metrics.

Example:

A sale contains three product line items:

- Product A: $30
- Product B: $20
- Product C: $10

If the customer should receive a $20 refund because they returned Product B, use `RefundProductSale` for the Product B Product Sale item.

This allows RevCent reporting and BigQuery analysis to show that Product B caused or received the refund. If the refund were only recorded at a broader sale or transaction level, product-level refund attribution would be less clear.

### Why this matters

Refunding at the Product Sale level helps answer questions like:

- Which products are refunded most often?
- Which campaigns are driving product-level refunds?
- Which shops have the highest product refund rate?
- Which products have high gross sales but low net sales after refunds?
- Which SKUs are causing chargeback, refund, or satisfaction issues?
- Which products are profitable after refunds and fees?

The line-item purchase aspect of Product Sales makes RevCent more useful for business analysis because product performance can be measured at the actual item purchased, not just the order total.

### Full refunds on initial sales

If the user is issuing a full refund for an entire initial sale, refunding each Product Sale individually may be redundant.

When a full initial sale refund is intended, the AI/MCP client should consider whether the more appropriate operation is a sale-level void/refund flow, such as `VoidSale`, depending on the user's intent and the sale state.

Use Product Sale refunds for full-sale refunds only when the user explicitly wants product-level refund entries for each line item or has a specific operational reason to refund each Product Sale individually.

### Summary rule

```text
Partial refund for one or more specific products:
Use RefundProductSale.

Full refund of the entire initial sale:
Refunding at the Product Sale level may be redundant unless product-level refund entries are explicitly desired.

Analytics question:
Use BigQueryRunQuery, not GetProductSales.
```

---

## Operation: GetProductSales

### Purpose

`GetProductSales` returns a paginated list of Product Sale records.

This operation is useful for finding Product Sale IDs and inspecting a limited set of Product Sales. It should not be used for analytics, aggregation, bulk retrieval, or metrics.

### Input schema

```json
{
  "date_start": 0,
  "date_end": 0,
  "limit": 25,
  "page": 1,
  "campaign_filter": ["XXXXXXXXXXXXXXXXXXXX"],
  "currency_filter": ["USD"],
  "shop_filter": ["XXXXXXXXXXXXXXXXXXXX"],
  "payment_type_filter": ["Credit Card"],
  "product_filter": ["XXXXXXXXXXXXXXXXXXXX"],
  "metadata_filter": [
    {
      "name": "example_name",
      "value": "example_value"
    }
  ],
  "customer_id": "XXXXXXXXXXXXXXXXXXXX"
}
```

### Required fields

| Field | Type | Notes |
|---|---:|---|
| `date_start` | integer | Unix timestamp in seconds. |
| `date_end` | integer | Unix timestamp in seconds. |
| `limit` | integer | 1-25. |
| `page` | integer | Pagination page. |

### Optional filters

| Field | Type | Purpose |
|---|---:|---|
| `campaign_filter` | array<string> | Filter by one or more Campaign IDs. |
| `currency_filter` | array<string> | Filter by ISO 4217 currency code, such as `USD`. |
| `shop_filter` | array<string> | Filter by one or more User Shop IDs. |
| `payment_type_filter` | array<string> | Filter by one or more payment types. |
| `product_filter` | array<string> | Filter by one or more Product IDs. |
| `metadata_filter` | array<object> | Filter by metadata name/value pairs. |
| `customer_id` | string | Filter to Product Sales related to a specific Customer ID. |

### Output highlights

The response includes pagination details and a `results` array. Each result can include:

- `id`
- amount fields
- `campaign_id`
- `campaign_name`
- `customer`
- `iso_currency`
- `live_mode`
- `payment_type`
- `product`
- `third_party_shop`
- `metadata`
- related entity arrays such as `sales`, `product_sales`, `transactions`, `pending_refunds`, and others

### Notable Product Sale fields

| Field | Meaning |
|---|---|
| `id` | The 20-character Product Sale ID. |
| `amount` | Product Sale amount. |
| `amount_original_total` | Original calculated total when the item was first created. |
| `amount_total` | Current total after refunds and discounts. |
| `amount_gross` | Money actually transacted, after refunded payments. |
| `amount_net` | Gross amount minus fees. |
| `amount_fees` | Estimated or calculated processing fees. |
| `amount_refunded` | Current amount refunded for the Product Sale. |
| `product` | Product details for the purchased line item. |
| `third_party_shop` | Originating User Shop, if applicable. |
| `metadata` | Metadata attached to the Product Sale. |
| `sales` | Related parent Sale IDs. |
| `pending_refunds` | Pending Refund IDs created from refund activity, if applicable. |

### Example: find recent Product Sales for a product

```json
{
  "date_start": 1735689600,
  "date_end": 1767225599,
  "limit": 25,
  "page": 1,
  "product_filter": ["XXXXXXXXXXXXXXXXXXXX"]
}
```

### Example: find Product Sales from a shop

```json
{
  "date_start": 1735689600,
  "date_end": 1767225599,
  "limit": 25,
  "page": 1,
  "shop_filter": ["XXXXXXXXXXXXXXXXXXXX"]
}
```

### AI/MCP guidance

When the user asks for Product Sale records, retrieve only the pages needed. When the user asks for totals, counts, summaries, trends, rankings, or product performance, do not page through `GetProductSales`. Use `BigQueryRunQuery`.

---

## Operation: GetProductSale

### Purpose

`GetProductSale` retrieves the full details of a specific Product Sale using its Product Sale ID.

Use this operation when:

- The user provides a Product Sale ID.
- A parent Sale contains `product_sales` IDs and the AI/MCP client needs line-item details.
- A refund may be issued against a specific Product Sale.
- The AI/MCP client needs to inspect `amount_refunded`, `amount_total`, product identity, shop origin, metadata, or related IDs.

### Input schema

```json
{
  "product_sale_id": "XXXXXXXXXXXXXXXXXXXX"
}
```

### Required fields

| Field | Type | Notes |
|---|---:|---|
| `product_sale_id` | string | 20-character Product Sale ID. |

### Output highlights

The response may include:

- Product Sale ID
- amount fields
- product object
- customer object
- campaign details
- payment type
- currency
- third-party shop
- metadata
- related Sale IDs
- related transaction, pending refund, chargeback, fraud, subscription, trial, and API call IDs

### Product object

The Product Sale response includes a `product` object representing the purchased line item:

```json
{
  "id": "XXXXXXXXXXXXXXXXXXXX",
  "name": "Example Product",
  "quantity": 1,
  "price": 29.99,
  "is_subscription": false,
  "is_trial": false,
  "internal_id": "internal-product-id",
  "sku": "SKU-123"
}
```

This product object is one of the most important parts of Product Sale analysis. It allows AI/MCP clients to connect revenue and refund outcomes to the exact product sold.

### Best practice before refunding

Before calling `RefundProductSale`, call `GetProductSale` unless the full Product Sale state is already known.

The AI/MCP client should inspect:

- Product identity
- Product Sale amount
- Existing `amount_refunded`
- Parent sale relationship
- Customer relationship
- Currency
- Whether the requested refund amount appears valid
- Whether the refund is intended to be on-platform or marked as refunded offsite

---

## Operation: RefundProductSale

### Purpose

`RefundProductSale` refunds a specific Product Sale.

If `amount` is provided, RevCent performs a partial refund for that amount. If `amount` is not provided, RevCent refunds the entire Product Sale amount.

### Input schema

```json
{
  "product_sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "amount": 10.00,
  "refunded_offsite": false
}
```

### Required fields

| Field | Type | Notes |
|---|---:|---|
| `product_sale_id` | string | 20-character Product Sale ID. |

### Optional fields

| Field | Type | Notes |
|---|---:|---|
| `amount` | number | Amount to refund. If omitted, the entire Product Sale amount is refunded. |
| `refunded_offsite` | boolean | If `true`, RevCent marks the Product Sale as refunded but does not contact the gateway. Default is `false`. |

### Output highlights

The response may include:

- `amount`
- `campaign_id`
- `campaign_name`
- `customer_id`
- `sale_id`
- `product_sale_id`
- `pending_refund`
- `result`

### Partial refund example

```json
{
  "product_sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "amount": 12.50
}
```

Use this when only part of a specific Product Sale should be refunded.

### Full Product Sale refund example

```json
{
  "product_sale_id": "XXXXXXXXXXXXXXXXXXXX"
}
```

Because no `amount` is provided, this refunds the entire Product Sale amount.

### Offsite refund example

```json
{
  "product_sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "amount": 25.00,
  "refunded_offsite": true
}
```

Use `refunded_offsite: true` only when the customer was already refunded outside RevCent, such as through Rapid Dispute Resolution, a mailed check, or another external process. This marks the refund in RevCent for reporting without initiating a gateway-level refund.

---

## Refund Decision Guide for AI/MCP Clients

### Scenario: Customer returns one product from a multi-product sale

Use `RefundProductSale`.

Steps:

1. Retrieve the Sale with `GetSale` if starting from a sale/order.
2. Identify the relevant Product Sale ID from the sale's `product_sales` array.
3. Retrieve the Product Sale with `GetProductSale`.
4. Confirm the product, amount, currency, and existing refund state.
5. Use `RefundProductSale` with the appropriate `amount`, or omit `amount` for a full refund of that product line.

### Scenario: Customer gets a small goodwill refund on one product

Use `RefundProductSale` with a partial `amount`.

This preserves product-level refund attribution.

### Scenario: Entire initial sale should be refunded

Do not automatically refund every Product Sale line item.

Refunding every Product Sale individually may be redundant for a full initial sale refund. Consider a sale-level refund/void operation, depending on the user's goal and sale state.

If the user explicitly wants product-level refund attribution on every line item, then refunding Product Sales individually may be appropriate.

### Scenario: User asks for product refund metrics

Use `BigQueryRunQuery`, not `GetProductSales`.

Example questions:

- “Which products have the highest refund rate?”
- “How much was refunded by product last month?”
- “Show product revenue net of refunds.”
- “Compare gross vs refunded revenue by SKU.”
- “Which shop has the worst product refund rate?”

### Scenario: User asks to inspect a specific Product Sale

Use `GetProductSale`.

---

## BigQuery and Business Analysis

Product Sales are ideal for BigQuery-based analysis because they represent the purchase line item. This gives cleaner product-level reporting than only analyzing Sale records.

Common analysis categories:

### Product revenue

- Gross product revenue
- Net product revenue
- Captured revenue
- Settled revenue
- Remaining revenue
- Salvage-related product amount

### Product refunds

- Refund amount by product
- Refund count by product
- Refund rate by product
- Refund amount by campaign
- Refund amount by shop
- Refund amount by payment type
- Refund amount by customer segment

### Product merchandising

- Top-selling products
- Products sold by shop
- Products sold by campaign
- Products with frequent discounts
- Subscription vs non-subscription product performance
- Trial product performance

### Operational analysis

- Product-level pending refunds
- Product-level chargebacks
- Product-level fraud detection relationships
- Product-level transaction relationships
- Product-level customer behavior

### AI/MCP BigQuery rule

Before writing BigQuery SQL, use `GetBigQueryTables`.

Do not assume the table name is exactly `product_sale`, `product_sales`, or any other value until the table schema has been retrieved. Use proper BigQuery table references in this form:

```sql
`revcent.user.<table_name>`
```

---

## Safe AI/MCP Workflow: Product-Level Refund

Use this workflow when the user wants to refund a product or line item.

### 1. Understand intent

Determine whether the user wants:

- a partial refund for a specific product,
- a full refund for a specific product line,
- a full refund of the entire sale,
- an offsite/reflected-only refund,
- or product-level refund reporting accuracy.

### 2. Locate the Product Sale

If the user provides a Product Sale ID:

```text
Call GetProductSale.
```

If the user provides a Sale ID:

```text
Call GetSale.
Review product_sales array.
Call GetProductSale for the relevant Product Sale ID.
```

If the user provides product/customer/order context but no IDs:

```text
Use appropriate search/list/get operations to identify the sale and Product Sale.
Do not guess.
```

### 3. Verify details

Before refunding, verify:

- Product name and ID.
- Product Sale ID.
- Customer.
- Parent Sale ID.
- Currency.
- Product Sale amount.
- Existing refunded amount.
- Requested refund amount.
- Whether refund should be gateway-level or offsite-only.

### 4. Choose refund type

Partial Product Sale refund:

```json
{
  "product_sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "amount": 10.00
}
```

Full Product Sale refund:

```json
{
  "product_sale_id": "XXXXXXXXXXXXXXXXXXXX"
}
```

Offsite/reflected refund:

```json
{
  "product_sale_id": "XXXXXXXXXXXXXXXXXXXX",
  "amount": 10.00,
  "refunded_offsite": true
}
```

### 5. Explain outcome

After refunding, summarize:

- Product refunded.
- Amount refunded.
- Whether it was offsite or processed through RevCent/gateway.
- Parent Sale ID.
- Any pending refund IDs returned.
- Why product-level refunding preserves better product metrics.

---

## AI/MCP Safety Rules

1. Do not issue a refund unless the user explicitly asks to refund.
2. Do not infer a refund amount without confirming or deriving it from the user’s request and Product Sale details.
3. Do not omit `amount` unless a full Product Sale refund is intended.
4. Do not use `refunded_offsite: true` unless the refund happened outside RevCent or the user explicitly wants a reporting-only refund.
5. Do not use `GetProductSales` for analytics or bulk data mining; use `BigQueryRunQuery`.
6. Do not assume the parent Sale should be fully refunded just because one Product Sale is being refunded.
7. Do not refund each Product Sale individually for a full initial sale refund unless product-level line-item refund entries are intentionally desired.
8. Always retrieve and inspect `GetProductSale` before calling `RefundProductSale` unless the Product Sale details are already known.
9. For sale-level context, use `GetSale` and then inspect its `product_sales` array.
10. For BigQuery reporting, always inspect table schemas first with `GetBigQueryTables`.

---

## Common Mistakes

### Mistake: Using Sale-level records for all product reporting

Sale-level records are useful for order-level analysis, but Product Sales are better for product-level metrics because they represent individual purchased products.

### Mistake: Using `GetProductSales` for totals

`GetProductSales` is paginated and operational. Use `BigQueryRunQuery` for totals, counts, and aggregations.

### Mistake: Refunding a whole Product Sale by accident

If `amount` is omitted in `RefundProductSale`, the entire Product Sale amount is refunded. Include `amount` for partial refunds.

### Mistake: Using offsite refunds incorrectly

`refunded_offsite: true` records a refund in RevCent without contacting the gateway. Only use it when the refund was already completed outside RevCent or when the user explicitly requests that behavior.

### Mistake: Losing product-level refund attribution

If a partial refund is related to a specific product but is issued only at a broader sale/transaction level, product-level refund metrics may become less useful. Prefer Product Sale-level refunds for product-specific partial refunds.

---

## Operation Summary

| Operation | Purpose | Best use |
|---|---|---|
| `GetProductSales` | List Product Sale records | Finding/inspecting limited Product Sale records |
| `GetProductSale` | Retrieve one Product Sale | Inspecting a line item before refunding or analysis |
| `RefundProductSale` | Refund one Product Sale | Product-specific partial or full line-item refunds |
| `BigQueryRunQuery` | Run analytics queries | Product-level metrics and business analysis |
| `GetSale` | Retrieve parent Sale | Finding Product Sale IDs from a sale/order |

---

## Final Guidance for AI/MCP Clients

Product Sales should be treated as the product-level financial line items of RevCent. They are not just child objects of a Sale; they are the best object for understanding how individual products perform.

For operational inspection, use `GetProductSales` and `GetProductSale`.

For product-specific refunds, especially partial refunds on initial sales, use `RefundProductSale`.

For full initial sale refunds, do not reflexively refund every Product Sale line item; that may be redundant unless the user explicitly wants product-level refund attribution for every line.

For analytics, use `BigQueryRunQuery` after retrieving schemas with `GetBigQueryTables`.

The line-item purchase design of Product Sales is what enables RevCent to provide strong business analysis around product performance, refunds, shops, campaigns, and customer behavior.


---
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.