{/* Metadata lives in ./layout.tsx */}

# Product costs

Time-versioned product cost catalog. Historical profit does not rewrite when costs change. {{ className: 'lead' }}

<Warning>
  Not live yet. Wire contract below is the intended v1 surface.
</Warning>

---

## `PUT /v1/products/costs`

**Scope:** `products:write`. **Auth:** `Authorization: Bearer sk_plp_...`

Body: batch of catalog rows upserted by natural key `(sku, effective_from)`.

```json
{
  "costs": [
    {
      "sku": "MUG-BLUE",
      "effective_from": "2026-01-01",
      "unit_cost": "41.20",
      "currency": "DKK"
    }
  ]
}
```

| Field | Required | Notes |
| --- | --- | --- |
| `sku` | yes | Catalog join key on order lines |
| `effective_from` | yes | `YYYY-MM-DD`; most recent version on or before the order date wins |
| `unit_cost` | yes | Non-negative decimal string |
| `currency` | yes | ISO-4217; a mismatch with the order currency leaves the line cost unresolved |

### How a line cost resolves

1. Inline `line_items[].unit_cost` sets `cost_source=inline` (never overwritten by catalog)
2. Else the catalog version with `effective_from` on or before the order `event_date` sets `cost_source=catalog`
3. Else `0` and `cost_source=none` (`cogs_complete=0` on the order)

### Late catalog pushes

Pushing a cost triggers a re-price sweep: open orders with `cost_source` of `catalog` or `none` for the affected SKUs are re-resolved from stored lines and re-upserted. Inline costs are left alone. No merchant re-send required.
