# `PhoenixKit.Email.ProviderOptions`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.13.11/lib/phoenix_kit/email/provider_options.ex#L1)

Per-provider send settings — the "Advanced" half of a `SendProfile`.

One source of truth for what each email provider actually accepts,
shared by the Send Settings form (which renders `fields_for/1`) and by
the delivery worker (which calls `to_provider_options/2` to turn a
profile's `advanced` map into Swoosh provider options).

Only options the underlying Swoosh adapter really reads are declared
here. An option the adapter ignores is worse than a missing one: it
looks configured and then silently does nothing — which is exactly what
the old free-form "Advanced (JSON)" textarea did, since nothing ever
read `advanced` at send time.

`smtp` declares no options on purpose: `Swoosh.Adapters.SMTP` takes all
its knobs (relay, port, TLS) from the connection itself and reads no
per-email `provider_options` at all. Rendering fields for it would be
the same lie in a new shape.

## Storage vs. wire format

`advanced` is a JSONB column, so it round-trips with **string** keys.
Swoosh, meanwhile, matches provider options on **atom** keys — and for
SES tags it pattern-matches `%{name: name, value: value}` strictly, so
a string-keyed tag map would raise inside the adapter. `cast/2` handles
the storage side, `to_provider_options/2` the wire side. Atoms come
from the `:option` field of a literal spec below, never from user
input, so there is no dynamic atom creation.

# `field`

```elixir
@type field() :: %{
  key: String.t(),
  option: atom(),
  label: String.t(),
  type: :text | :number,
  cast: :string | :integer | :string_list | :name_value_list,
  placeholder: String.t(),
  help: String.t()
}
```

# `cast`

```elixir
@spec cast(String.t() | nil, map() | nil) :: map()
```

Normalizes raw form input into the map stored in `advanced`.

Keeps only keys the provider declares, so flipping a profile from SES to
Brevo can't leave an orphan `configuration_set_name` behind to be sent to
an adapter that has no idea what it is. Blank values are dropped rather
than stored as `""`, so an untouched field stores nothing at all.

# `fields_for`

```elixir
@spec fields_for(String.t() | nil) :: [field()]
```

Returns the send settings a given provider supports, in display order.

An unknown provider returns `[]` rather than raising: a profile whose
integration was deleted, or one pointing at a provider some other module
registered, must still render its common fields instead of 500ing.

# `to_input_value`

```elixir
@spec to_input_value(field(), map() | nil) :: String.t()
```

Renders a stored value back into the text the form input shows.

The inverse of `cast_value/2` for the list types, so editing a profile
shows `campaign=newsletter, env=prod` rather than a raw JSON array.

# `to_provider_options`

```elixir
@spec to_provider_options(String.t() | nil, map() | nil) :: map()
```

Translates a profile's stored `advanced` map into Swoosh provider options.

Returns a map keyed by the atoms Swoosh expects. Anything not declared by
the provider is ignored — including leftovers written by the old raw-JSON
textarea, which must never reach an adapter unvetted.

---

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