# `LatticeStripe.Product.Feature`
[🔗](https://github.com/szTheory/lattice_stripe/blob/v2.2.2/lib/lattice_stripe/product/feature.ex#L1)

Stripe Product Feature attachments.

A `%Feature{}` is the `product_feature` attachment (an id prefixed `prodft_`)
between a Product (`prod_`) and an `LatticeStripe.Entitlements.Feature`
definition (`feat_`). It is not the Product's `features` or
`marketing_features` display copy.

Use the canonical `create/4`, `retrieve/4`, `list/4`, `stream!/4`, and
`delete/4` resource verbs in code. In domain prose, an attachment is something
you *attach* to or *remove* from a Product; this module intentionally publishes
no parallel `attach`, `detach`, or `remove` aliases. `list/4` and `stream!/4`
are the authoritative catalog reads for access-bearing attachments.

See `LatticeStripe.Entitlements.Feature` for the definition side of the
relationship, and [Entitlements](guides/entitlements.md) for the
catalog-to-access lifecycle.

# `t`

```elixir
@type t() :: %LatticeStripe.Product.Feature{
  deleted: boolean(),
  entitlement_feature: LatticeStripe.Entitlements.Feature.t() | nil,
  extra: map(),
  id: String.t() | nil,
  livemode: boolean() | nil,
  object: String.t() | nil
}
```

# `create`

```elixir
@spec create(LatticeStripe.Client.t(), String.t(), map(), keyword()) ::
  {:ok, t()} | {:error, LatticeStripe.Error.t()}
```

Attach an entitlement feature definition to a Product.

`params` must contain the string key `"entitlement_feature"`; it has no
default because Stripe's attachment endpoint requires that definition ID.

# `create!`

```elixir
@spec create!(LatticeStripe.Client.t(), String.t(), map(), keyword()) :: t()
```

Bang variant of `create/4`. Raises `LatticeStripe.Error` on failure.

# `delete`

```elixir
@spec delete(LatticeStripe.Client.t(), String.t(), String.t(), keyword()) ::
  {:ok, t()} | {:error, LatticeStripe.Error.t()}
```

Delete one Product Feature attachment by its `prodft_` id.

# `delete!`

```elixir
@spec delete!(LatticeStripe.Client.t(), String.t(), String.t(), keyword()) :: t()
```

Bang variant of `delete/4`. Raises `LatticeStripe.Error` on failure.

# `from_map`

```elixir
@spec from_map(map() | t() | nil) :: t() | nil
```

Decode a Stripe Product Feature attachment map.

# `list`

```elixir
@spec list(LatticeStripe.Client.t(), String.t(), map(), keyword()) ::
  {:ok, LatticeStripe.Response.t()} | {:error, LatticeStripe.Error.t()}
```

List Product Feature attachments for a Product.

# `list!`

```elixir
@spec list!(LatticeStripe.Client.t(), String.t(), map(), keyword()) ::
  LatticeStripe.Response.t()
```

Bang variant of `list/4`. Raises `LatticeStripe.Error` on failure.

# `retrieve`

```elixir
@spec retrieve(LatticeStripe.Client.t(), String.t(), String.t(), keyword()) ::
  {:ok, t()} | {:error, LatticeStripe.Error.t()}
```

Retrieve one Product Feature attachment by its `prodft_` id.

# `retrieve!`

```elixir
@spec retrieve!(LatticeStripe.Client.t(), String.t(), String.t(), keyword()) :: t()
```

Bang variant of `retrieve/4`. Raises `LatticeStripe.Error` on failure.

# `stream!`

```elixir
@spec stream!(LatticeStripe.Client.t(), String.t(), map(), keyword()) ::
  Enumerable.t()
```

Lazily enumerate every Product Feature attachment for a Product.

Pagination mechanics are delegated to `LatticeStripe.List.stream!/2`; each
attachment is decoded as it is emitted. There is intentionally no non-bang
stream variant because later-page failures raise while a stream is consumed.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
