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

# Get products

> List the products selected in a discovery session

`GET /api/v2/buyer/discovery/{discoveryId}/products`

Returns the products currently selected in the session, with their per-product budget allocations and budget context. This is the selection you build with [Add products](/v2/buyer/discovery/tasks/add-products) and [Apply proposal](/v2/buyer/discovery/tasks/apply-proposal) — distinct from the browsable discovery results.

## Request

```bash theme={null}
curl -X GET "https://api.apostra.com/api/v2/buyer/discovery/disc_01HZX3YQ7K9R6V3M2P1E0F8B2T/products" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
```

## Parameters

| Field         | Type   | Required | Notes                        |
| ------------- | ------ | -------- | ---------------------------- |
| `discoveryId` | string | Yes      | Path param — session to read |

## Response

```json theme={null}
{
  "discoveryId": "disc_01HZX3YQ7K9R6V3M2P1E0F8B2T",
  "products": [
    {
      "productId": "prod_acme_ctv_sports",
      "salesAgentId": "agent_acme_media",
      "bidPrice": 32.5,
      "budget": 150000,
      "groupId": "ctx_disc_01HZX3-group-0",
      "groupName": "Acme Media Connected TV",
      "selectedAt": "2026-06-07T15:04:00Z"
    }
  ],
  "totalProducts": 1,
  "budgetContext": { "sessionBudget": 250000, "allocatedBudget": 150000, "remainingBudget": 100000 }
}
```

`budgetContext.remainingBudget` is the session budget minus the sum of selected `budget` allocations. An empty `products` array means nothing has been added yet.

## Errors

* `404 NOT_FOUND` — `discoveryId` does not exist or is not visible to the authenticated account.
* `409 BUYER_SETUP_REQUIRED` — the buyer account has no confirmed,
  non-platform operator identity. The structured details are
  `reason: "missing_operator_identity"`, `readinessCheck:
  "operator_identity"`, `setupStep: 1`, and `setupPage: "buyer_setup"`.
  Open **Buyer Setup**, confirm the operator domain and account scope, then
  retry. Apostra fails closed and does not substitute `interchange.io` as
  the buyer operator.

  If a legacy profile explicitly contains the platform domain, the same code
  is returned with `details.reason: "platform_operator_not_allowed"`. Complete
  Buyer Setup with your organization’s operator domain; do not retry with the
  platform domain.

```json theme={null}
{
  "data": null,
  "error": {
    "code": "BUYER_SETUP_REQUIRED",
    "message": "Complete Buyer Setup step 1 by confirming your buyer operator identity before continuing.",
    "details": {
      "reason": "missing_operator_identity",
      "readinessCheck": "operator_identity",
      "setupStep": 1,
      "setupPage": "buyer_setup"
    }
  }
}
```

See [Errors](/v2/reference/errors) for the full error contract.

## Related

<CardGroup cols={2}>
  <Card title="Discovery overview" href="/v2/guides/discovery" icon="rocket">
    Sessions and selection model
  </Card>

  <Card title="Add products" href="/v2/buyer/discovery/tasks/add-products" icon="plus">
    Add products to the selection
  </Card>

  <Card title="Remove products" href="/v2/buyer/discovery/tasks/remove-products" icon="trash">
    Drop products from the selection
  </Card>

  <Card title="Apply proposal" href="/v2/buyer/discovery/tasks/apply-proposal" icon="wand-magic-sparkles">
    Accept a seller's full allocation
  </Card>
</CardGroup>
