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

Behaviour contract for outbound delivery adapters.

## Content responsibility

Adapters receive a pre-planned `%Chimeway.Delivery{}` struct with all rendered
content already present. Adapters MUST NOT call back into notifier modules to
render content — all content must arrive pre-populated on the delivery struct
before `deliver/2` is called.

## Configuration

Adapter config (API keys, base URLs, from addresses, etc.) must be read at
call time via `Application.get_env/3`. Never read config in module attributes
or at compile time — this supports test overrides via `Application.put_env`
and runtime environment switching.

## Return contract

- `{:ok, meta}` — the provider accepted the delivery. `meta` is a compact
  map written to `chimeway_delivery_attempts.provider_response`. Adapters MUST
  redact sensitive fields (password, token, secret, api_key, auth) from `meta`
  before returning.

- `{:error, reason_class, detail}` — delivery failed. `reason_class` MUST be
  one of `:temporary | :permanent | :bounced`:
  - `:temporary` — transient failure; the dispatcher may retry.
  - `:permanent` — non-retriable rejection by the provider.
  - `:bounced` — the address or identity is unreachable (e.g. hard bounce).
  `detail` is a compact map with no PII and no full provider response bodies.

## Outcome classification

Outcome classification (`:succeeded`, `:failed`, `:rejected`, `:bounced`) is
the dispatcher's responsibility, not the adapter's. Adapters return raw results;
`Chimeway.Dispatch.Sync` maps them to Chimeway's delivery state machine.

# `deliver`

```elixir
@callback deliver(delivery :: Chimeway.Delivery.t(), config :: keyword()) ::
  {:ok, map()} | {:error, atom(), map()}
```

Deliver a single delivery to its outbound channel.

`delivery` is a pre-planned `%Chimeway.Delivery{}` struct with all content
populated. `config` is a keyword list of adapter-specific options read from
`Application.get_env/3` at call time.

# `normalize_feedback`
*optional* 

```elixir
@callback normalize_feedback(parsed_payload :: map()) ::
  {:ok, %{status: :delivered | :bounced | :failed}} | :error
```

Map a parsed provider webhook payload into a normalized canonical Chimeway outcome.
Required for adapters supporting async feedback loops.

# `resolve_delivery`
*optional* 

```elixir
@callback resolve_delivery(parsed_payload :: map()) ::
  {:ok, %{delivery_id: binary()}}
  | {:ok, %{provider_message_id: String.t()}}
  | :error
```

Extract the delivery identity from a parsed provider webhook payload.
Required for adapters supporting async feedback loops.

# `resolve_provider_event_id`
*optional* 

```elixir
@callback resolve_provider_event_id(parsed :: map()) :: {:ok, binary()} | :none
```

Extract a stable provider-assigned event ID from a parsed provider webhook payload.
Used for idempotent deduplication of provider retries (Phase 33 D-05 / A4).
Optional: adapters without stable event IDs omit this callback; those callbacks
get `provider_event_id = nil` (no dedup — the partial unique index ignores NULLs).

# `verify_webhook`
*optional* 

```elixir
@callback verify_webhook(raw_body :: binary(), headers :: list(), config :: keyword()) ::
  :ok | {:error, :unauthorized}
```

Verify the cryptographic signature of an incoming webhook from the provider.
Required for adapters supporting async feedback loops.

---

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