# POST /opportunities/:opportunity_id/products

**Resource:** [Opportunities](./opportunities.md)  
**MCP tool:** `add_opportunity_product`  
**Scopes:** `opportunities:write`  
**Write operation:** yes

Attach a product to an opportunity.

## Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `opportunity_id` | path | string | yes |  |
| `product_id` | body | string | no |  |
| `quantity` | body | number | no |  |
| `unit_price` | body | number | no |  |
| `discount_percent` | body | number | no |  |
| `deposit_percent` | body | number | no |  |
| `bucket_id` | body | string | no | Optional product bucket id to file this line item under (create buckets with create_opportunity_product_bucket). Buckets group line items by area (e.g. "Main Bathroom") with their own subtotal, and let the same product appear in more than one bucket. |
| `invoice_description` | body | string | no | The exact text this line prints on the invoice: the "Invoice description" field on the line item's Details tab. Use it to describe the work actually done for this customer instead of the generic catalogue wording. Leave it unset and the line falls back to the catalogue product name plus its description, as before. Pass an empty string to clear an override and go back to the catalogue text. |
| `require_approval` | body | boolean | no | Optional. Set true to route this write into the approval queue for human review instead of executing it immediately (returns 202 + an approval_id). Works even when your key/token has permission to execute directly. |

## Request example

```bash
curl -X POST   "https://api.trustpager.com/functions/v1/api/v1/opportunities/:opportunity_id/products"   -H "Authorization: Bearer YOUR_API_KEY"   -H "Content-Type: application/json"   -d '{"product_id":"...","quantity":"...","unit_price":"..."}'
```

---
Base URL: `https://api.trustpager.com/functions/v1/api/v1` — Auth: `Authorization: Bearer YOUR_API_KEY`