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

# Auto-select products

> Let Apostra pick a balanced product set for a DRAFT campaign

`POST /api/v2/buyer/campaigns/:id/auto-select-products`

For `DRAFT` campaigns with an attached discovery session, this lets Apostra pick a balanced product set. Iterate with ADCP-style refinement to nudge the selection toward your plan.

## Request

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.apostra.com/api/v2/buyer/campaigns/cmp_987654321/auto-select-products \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "maxProducts": 8, "minBudgetPerProduct": 2500 }'
  ```

  ```json With refinement theme={null}
  {
    "maxProducts": 8,
    "minBudgetPerProduct": 2500,
    "refine": [
      { "scope": "request", "ask": "more video, less display" },
      { "scope": "product", "id": "prod_xyz789", "action": "moreLikeThis" },
      { "scope": "product", "id": "prod_xyz790", "action": "omit" }
    ]
  }
  ```
</CodeGroup>

## Parameters

| Field | Type | Required | Notes |
| - | - | - | - |
| `id` | string | Yes | Campaign ID (path parameter). Must be `DRAFT` with a discovery session. |
| `maxProducts` | number | No | Maximum number of products to select. |
| `minBudgetPerProduct` | number | No | Minimum budget each selected product must support. |
| `refine` | array | No | ADCP-style refinement instructions applied to the next selection pass. |
| `refine[].scope` | `request` \| `product` | Yes (per entry) | `request` refines the whole selection; `product` targets a single product. |
| `refine[].ask` | string | For `request` scope | Free-text instruction, e.g. `"more video, less display"`. |
| `refine[].id` | string | For `product` scope | Product ID to act on. |
| `refine[].action` | `include` \| `omit` \| `moreLikeThis` | For `product` scope | `include` keeps this product; `omit` drops it; `moreLikeThis` finds similar ones. |

## Response

```json theme={null}
{
  "campaignId": "cmp_987654321",
  "discoveryId": "disc_abc123",
  "selectedProducts": [
    {
      "productId": "prod_xyz789",
      "name": "Example Media - CTV Premium",
      "salesAgentId": "agent_example",
      "groupId": "grp_1",
      "groupName": "CTV",
      "cpm": 18.5,
      "budget": 25000,
      "goalCoverage": {
        "coversPrimaryGoal": true,
        "uncovered": [],
        "expected": {
          "basis": "completion_rate",
          "low": 0.62,
          "mid": 0.66,
          "high": 0.71,
          "required": 0.5,
          "verdict": "likely"
        }
      }
    }
  ],
  "budgetContext": {
    "campaignBudget": 100000,
    "totalAllocated": 25000,
    "remainingBudget": 75000,
    "currency": "USD"
  },
  "productCount": 1
}
```

When the campaign has optimization goals, auto-select prefers products that
can optimize to the primary goal: every product that can ranks ahead of every
product that cannot. Among the products that can, those whose forecast makes
the primary goal's target `likely` come first, then `possible`, then products
with no expectation, then `unlikely`. The usual order (historical score, or
lowest CPM) decides within each group. The preference applies before `maxProducts` or
`minBudgetPerProduct` trims the list. A product that cannot optimize to the
goal is still selected when the budget reaches past every product that can,
and products you `include` are kept whatever they can optimize.

`goalCoverage` on each selected product says what it can optimize, read from
the product's AdCP optimization declaration:

| Field | Meaning |
| - | - |
| `selectedProducts[].goalCoverage.coversPrimaryGoal` | Whether the product can optimize to the primary goal (priority 1). |
| `selectedProducts[].goalCoverage.uncovered[]` | Each goal the product cannot optimize to: the `goal`, a `code` (for example `metric_not_declared` or `goal_kind_not_declared`) and a readable `reason`. Empty when it can optimize to every goal. |
| `selectedProducts[].goalCoverage.expected` | How the product is expected to perform against a `views`, `viewable_rate` or `completed_views` primary goal with a target, from the seller's forecast. Absent when there is nothing to judge. |
| `selectedProducts[].goalCoverage.expected.basis` | What the rates measure: `viewable_rate` (viewable ÷ measurable), `viewable_per_impression` (viewable ÷ all impressions, for a cost per viewable impression) or `completion_rate` (completed views ÷ impressions). |
| `selectedProducts[].goalCoverage.expected.low` / `mid` / `high` | The expected rate, 0 to 1: conservative, expected and optimistic. |
| `selectedProducts[].goalCoverage.expected.required` | The rate the goal needs: a `threshold_rate` target, or CPM ÷ (1000 × target) for a `cost_per` target at the product's CPM. |
| `selectedProducts[].goalCoverage.expected.verdict` | `likely` (the low end meets `required`), `possible` (only the expected or high end does) or `unlikely` (the high end misses). |

`goalCoverage` is absent when the campaign has no goals or the platform holds
no copy of the product. A product that declares no optimization capability
covers no goal. See
[Goal-seeking campaigns](/v2/concepts/goal-seeking-campaigns#expected-performance-against-your-goal)
for how the expectation is worked out.

Read the confirmed selection with [Get campaign products](/v2/buyer/campaigns/tasks/get-campaign-products).

## Errors

| Code | When |
| - | - |
| `VALIDATION_ERROR` | Campaign is not `DRAFT`, has no discovery session, or `refine` entries are malformed. |
| `NOT_FOUND` | Campaign ID does not exist for the authenticated account. |

See [Errors](/v2/reference/errors) for the full error shape and recovery semantics.

## Related

<CardGroup cols={2}>
  <Card title="Discovery" href="/v2/guides/discovery" icon="magnifying-glass">
    Build the discovery session that feeds selection
  </Card>

  <Card title="Get campaign products" href="/v2/buyer/campaigns/tasks/get-campaign-products" icon="boxes-stacked">
    Read the selected products
  </Card>

  <Card title="Execute campaign" href="/v2/buyer/campaigns/tasks/execute-campaign" icon="rocket">
    Launch the campaign into media buys
  </Card>

  <Card title="Campaign overview" href="/v2/object-guides/campaign" icon="rocket">
    The campaign object and lifecycle
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.