> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adcontextprotocol.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Refinement

> AdCP product refinement — iterate on discovered products and proposals using refine mode in get_products. Adjust selections and request changes before creating a media buy.

Refinement turns product discovery into a conversation. After an initial `brief` or `wholesale` discovery, use `buying_mode: "refine"` to iterate on specific products and proposals — adjusting the selection, requesting changes, and exploring alternatives — before committing to a [`create_media_buy`](/dist/docs/3.0.0-rc.2/media-buy/task-reference/create_media_buy).

## The refinement lifecycle

A typical media buying workflow follows this pattern:

```
discover → refine → refine → ... → buy
```

1. **Discover** — Call `get_products` with `buying_mode: "brief"` or `"wholesale"` to find matching inventory. The seller returns products (and optionally proposals).

2. **Refine** — Call `get_products` with `buying_mode: "refine"` and a `refine` array of change requests. Each entry declares a scope and what the buyer is asking for. The seller returns updated products with revised pricing and configurations.

3. **Repeat** — Refine as many times as needed. Each call is self-contained and stateless.

4. **Buy** — When satisfied, execute the final selection via `create_media_buy`.

<Tip>
  Refinement is not required. Simple campaigns can go straight from discovery to purchase. But for campaigns involving multiple products, proposals with budget allocations, or iterative negotiation, refinement is where the value is.
</Tip>

## The refine array

The `refine` array is a list of change requests. Each entry declares a `scope` and what the buyer is asking for:

| Scope      | Purpose                                | Required fields |
| ---------- | -------------------------------------- | --------------- |
| `request`  | Direction for the selection as a whole | `ask`           |
| `product`  | Action on a specific product           | `id`, `action`  |
| `proposal` | Action on a specific proposal          | `id`, `action`  |

The `refine` array requires at least one entry. The seller considers all entries together when composing the response, and replies to each one via `refinement_applied`.

### Product actions

Product-scoped entries declare an explicit action:

| Action           | Behavior                                                                             | `ask`                                                                    |
| ---------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| `include`        | Return this product with updated pricing and data                                    | Optional — specific changes to request (e.g., "add 16:9 format")         |
| `omit`           | Exclude this product from the response                                               | Ignored                                                                  |
| `more_like_this` | Find additional products similar to this one. The original product is also returned. | Optional — what "similar" means (e.g., "same audience but video format") |

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "id": "prod_video_premium", "action": "include", "ask": "add 16:9 format option" },
    { "scope": "product", "id": "prod_display_ros", "action": "omit" },
    { "scope": "product", "id": "prod_native", "action": "more_like_this", "ask": "same audience but video format" }
  ]
}
```

### Request-level direction

Use `scope: "request"` to describe what you want from the selection as a whole:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "request", "ask": "good selection but I want more video options and less display" },
    { "scope": "product", "id": "prod_video_premium", "action": "include" },
    { "scope": "product", "id": "prod_display_ros", "action": "include" },
    { "scope": "product", "id": "prod_native", "action": "include" }
  ]
}
```

The seller may add, remove, or rebalance products based on this direction. Products not referenced in the `refine` array may appear in the response if the seller determines they fit the direction.

**Precedence**: Per-product actions take precedence over request-level direction. If the request-level ask says "less display" but a specific product has `action: "include"`, that product is returned regardless.

### Proposal refinement

Reference proposals by ID to request adjustments or remove them:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "id": "prod_video_premium", "action": "include" },
    { "scope": "product", "id": "prod_display_ros", "action": "include" },
    { "scope": "proposal", "id": "prop_balanced_v1", "action": "include", "ask": "shift 20% from display to video" }
  ]
}
```

### Combining scopes

All scopes work together. A single refinement call can set direction for the selection, act on specific products, and request changes to a proposal:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "request", "ask": "increase emphasis on video across the plan" },
    { "scope": "product", "id": "prod_video_premium", "action": "include" },
    { "scope": "product", "id": "prod_display_ros", "action": "include" },
    { "scope": "product", "id": "prod_native", "action": "omit" },
    { "scope": "product", "id": "prod_audio_spot", "action": "include" },
    { "scope": "proposal", "id": "prop_awareness_q2", "action": "include", "ask": "reallocate native budget to video products" }
  ],
  "filters": {
    "budget_range": { "min": 200000, "max": 200000, "currency": "USD" }
  }
}
```

## Seller response

When a buyer sends a `refine` array, the seller responds with `refinement_applied` — an array matched by position to the buyer's change requests. Each entry reports whether the ask was fulfilled:

| Field    | Type   | Required | Description                                                                                               |
| -------- | ------ | -------- | --------------------------------------------------------------------------------------------------------- |
| `scope`  | string | No       | Echoes the scope from the corresponding `refine` entry. Allows orchestrators to cross-validate alignment. |
| `id`     | string | No       | Echoes the id from the corresponding `refine` entry (for product and proposal scopes).                    |
| `status` | string | Yes      | `"applied"`: ask fulfilled. `"partial"`: partially fulfilled. `"unable"`: could not fulfill.              |
| `notes`  | string | No       | Seller explanation. Recommended when status is `"partial"` or `"unable"`.                                 |

```json theme={null}
{
  "products": ["..."],
  "proposals": ["..."],
  "refinement_applied": [
    { "scope": "request", "status": "applied", "notes": "Added 3 video products. No CTV inventory for those dates." },
    { "scope": "product", "id": "prod_video_premium", "status": "applied" },
    { "scope": "product", "id": "prod_display_ros", "status": "applied" },
    { "scope": "product", "id": "prod_native", "status": "applied" },
    { "scope": "product", "id": "prod_audio_spot", "status": "partial", "notes": "16:9 not available for this placement — returning 4:3 and 1:1" },
    { "scope": "proposal", "id": "prop_awareness_q2", "status": "applied", "notes": "Shifted 22% to video (nearest allocation boundary)" }
  ]
}
```

The `refinement_applied` array MUST contain the same number of entries in the same order as the `refine` array. Entries SHOULD echo `scope` and `id` for cross-validation. The entire field is optional — sellers that don't track per-ask outcomes can omit it.

## Common refinement patterns

### Find similar products

Use `more_like_this` to discover products similar to ones you like. The seller returns the original product plus additional options matching its characteristics:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "id": "prod_video_premium", "action": "more_like_this", "ask": "same premium audience but different formats" }
  ]
}
```

### Adjust filters

Filters on a refine request represent the complete target state, not a delta. Always send the full filter set you want applied:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "id": "prod_video_premium", "action": "include" },
    { "scope": "product", "id": "prod_display_ros", "action": "include" }
  ],
  "filters": {
    "start_date": "2026-04-01",
    "end_date": "2026-06-30",
    "budget_range": { "min": 150000, "max": 150000, "currency": "USD" }
  }
}
```

### Narrow or expand a proposal

The product entries define which products the seller should consider for the proposal. Combined with proposal entries, this narrows or expands the proposal's product set:

```json theme={null}
{
  "buying_mode": "refine",
  "refine": [
    { "scope": "product", "id": "prod_video_premium", "action": "include" },
    { "scope": "product", "id": "prod_display_ros", "action": "include" },
    { "scope": "proposal", "id": "prop_balanced_v1", "action": "include", "ask": "rebalance for just these two products" }
  ]
}
```

## Proposals in refine mode

Sellers MAY return proposals alongside refined products, even when the buyer did not include proposal entries. For example, a buyer refining three products may receive those products back with updated pricing *and* a proposal suggesting how to combine them.

Key points:

* **Proposals are not guaranteed.** Sellers are not required to generate proposals in refine mode. Allocation and campaign optimization are primarily orchestrator (buyer-side agent) responsibilities.
* **Signal interest via a request-level ask.** Include `{ "scope": "request", "ask": "suggest how to combine these products" }` to indicate you'd welcome a proposal.
* **Unsolicited proposals can be refined or ignored.** If a seller returns a proposal you didn't request, you can refine it in a follow-up call, or simply ignore it and build packages manually via `create_media_buy`.

Publishers typically omit proposals in `wholesale` mode, where the buyer is directing targeting and allocation themselves.

## Statelessness

Each `get_products` request with `buying_mode: "refine"` is self-contained. The `refine` array and `filters` on each request fully specify the refinement intent. Sales agents MUST NOT depend on transport-level session state (e.g., remembering what was sent in a previous request).

Sellers still maintain their own product and proposal registries — "stateless" means the *protocol exchange* carries no implicit state between calls.

This design enables:

* **Stateless implementations** — sellers don't need to track refinement sessions
* **Safe retries** — a failed refinement call can be retried with the same parameters
* **Parallel exploration** — an orchestrator can explore multiple refinement paths simultaneously

## Client validation

Orchestrators should validate refinement requests before sending:

* **Non-empty refine** — The `refine` array requires at least one entry. An empty `[]` is rejected by schema validation.
* **Valid entries** — Each product entry requires `scope`, `id`, and `action` (`include`, `omit`, or `more_like_this`). Each proposal entry requires `scope`, `id`, and `action` (`include` or `omit`). Request-level entries require `scope` and `ask`.
* **Filters are absolute** — Send the full filter set you want applied, not a delta from the previous request.

Client implementations should validate refinement requests against the [request schema](/dist/docs/3.0.0-rc.2/building/schemas-and-sdks) before sending.

## Error handling

| Error Code          | When                                                                                               | Resolution                                                          |
| ------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `PRODUCT_NOT_FOUND` | One or more referenced product IDs are unknown or expired                                          | Remove invalid IDs and retry, or re-discover with a `brief` request |
| `PROPOSAL_EXPIRED`  | A referenced proposal ID has passed its `expires_at`                                               | Re-discover with a new `brief` or `wholesale` request               |
| `INVALID_REQUEST`   | `refine` provided in `brief` or `wholesale` mode, empty `refine` array, or missing required fields | Check `buying_mode` and required fields                             |

## Normative requirements

The [Media Buy Specification](/dist/docs/3.0.0-rc.2/media-buy/specification#get_products) defines the following normative requirements for refinement:

**Orchestrators:**

* MUST include `refine` when `buying_mode` is `"refine"`
* MUST NOT include `refine` when `buying_mode` is `"brief"` or `"wholesale"`
* MUST provide `scope`, `id`, and `action` for each product and proposal entry
* MUST NOT include multiple entries for the same product ID or proposal ID in a single `refine` array

**Sales agents:**

* MUST omit products with `action: "omit"` from the response
* MUST omit proposals with `action: "omit"` from the response
* MUST return products with `action: "include"`, with updated pricing
* SHOULD fulfill the `ask` on product entries with `action: "include"`
* SHOULD return additional products similar to those with `action: "more_like_this"`, plus the original product
* SHOULD consider request-level asks when composing the response — this MAY result in additional products beyond those explicitly referenced. Per-product actions take precedence over request-level direction.
* SHOULD fulfill the `ask` on proposal entries with `action: "include"`
* SHOULD include `refinement_applied` in the response when the buyer provides `refine`, with one entry per change request matched by position
* MAY return proposals even when the buyer did not include proposal entries

## See also

* [`get_products` task reference](/dist/docs/3.0.0-rc.2/media-buy/task-reference/get_products#refinement) — API reference with request/response schemas
* [Media Products](/dist/docs/3.0.0-rc.2/media-buy/product-discovery/media-products) — product model and proposal structure
* [Media Buy Specification](/dist/docs/3.0.0-rc.2/media-buy/specification#get_products) — normative requirements
* [Orchestrator Design](/dist/docs/3.0.0-rc.2/building/implementation/orchestrator-design) — building buyer-side agents
