# `Chimeway.Preferences`
[🔗](https://github.com/jonlunsford/chimeway/blob/v1.0.0/lib/chimeway/preferences.ex#L1)

Public context for notification preference management.

Preferences are keyed by (recipient_id, notification_key, channel).
`recipient_id` is the same string as `recipient_identity` on notification rows.

Missing preferences default to enabled — channels are opt-in by default.

# `category_enabled?`

```elixir
@spec category_enabled?(String.t(), String.t()) :: boolean()
```

Returns true if the category is enabled for the recipient — defaults to true
when no preference row exists (opt-in default).

# `channel_enabled?`

```elixir
@spec channel_enabled?(String.t(), String.t(), String.t()) :: boolean()
```

Returns true if the channel is enabled for the recipient/key — defaults to
true when no preference row exists (opt-in default).

# `get_category_preference`

```elixir
@spec get_category_preference(String.t(), String.t()) ::
  Chimeway.Preferences.CategoryPreference.t() | nil
```

Fetches the category preference row for the given recipient/category, or nil.

# `get_preference`

```elixir
@spec get_preference(String.t(), String.t(), String.t()) ::
  Chimeway.Preferences.NotificationPreference.t() | nil
```

Fetches the preference row for the given recipient/key/channel, or nil.

# `upsert_category_preference`

```elixir
@spec upsert_category_preference(map()) ::
  {:ok, Chimeway.Preferences.CategoryPreference.t()}
  | {:error, Ecto.Changeset.t()}
```

Upserts a category preference. On conflict, updates :enabled and :updated_at.

# `upsert_preference`

```elixir
@spec upsert_preference(map()) ::
  {:ok, Chimeway.Preferences.NotificationPreference.t()}
  | {:error, Ecto.Changeset.t()}
```

Upserts a preference. On conflict, updates :enabled and :updated_at.

---

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