# `SquatchMail.Capture`
[🔗](https://github.com/Axio-Intelligence/Squatch-Mail/blob/v0.1.0/lib/squatch_mail/capture.ex#L1)

Observes every `Swoosh.Mailer.deliver/2` and `deliver_many/2` call in the
host application via `:telemetry`, with zero changes to the host's mailer
or adapter.

`Swoosh.Mailer` wraps each send in `:telemetry.span/3`, emitting
`[:swoosh, :deliver | :deliver_many, :start | :stop | :exception]`. This
module attaches to `:stop` and `:exception` only — never `:start`, since
there's nothing to persist until a send has actually finished — and
records the outcome through `SquatchMail.Capture.Recorder`.

## Design constraints

Telemetry handlers run **synchronously in the caller's process** — the
same process that just called `Mailer.deliver/2`. This module therefore:

  * never raises: every step is wrapped and logged on failure rather than
    propagated, so a SquatchMail bug can never break the host's mail
    sending;
  * never blocks the caller: the handler only builds attrs and hands them
    to `SquatchMail.Capture.Recorder.record/1` (a `GenServer.cast/2`),
    returning immediately regardless of how long the actual database write
    takes, or whether the queue is full and the attrs get dropped.

## Adapter result normalization

The `result` telemetry metadata is adapter-specific. This module
recognizes:

  * `Swoosh.Adapters.AmazonSES` and `Swoosh.Adapters.ExAwsAmazonSES`
    (which delegates straight to `AmazonSES`, producing the same shape):
    `%{id: message_id, request_id: request_id}` — atom-keyed.
  * Raw `AWS.SESv2.send_email/3` responses (for hosts using a custom
    adapter built directly on the `aws` package): `%{"MessageId" =>
    message_id}` — string-keyed, PascalCase, straight off the wire.

Any other adapter's result is still recorded — just without a
`message_id`, which means SES event ingestion won't be able to correlate
delivery/bounce events back to it later. Unrecognized shapes are never
treated as an error.

## Configuration

See `SquatchMail.Config` for `:enabled` and the nested `:capture` options
(`:store_html`, `:store_text`, `:sample_rate`, `:max_queue`).

# `attach`

```elixir
@spec attach() :: :ok
```

Attaches the capture handler.

No-ops (logging nothing, doing nothing) when Swoosh isn't loaded — it's an
optional dependency, and a host observing nothing through it is a valid
configuration, not an error. Safe to call more than once: re-attaching
detaches the previous handler first rather than erroring or double-firing.

# `detach`

```elixir
@spec detach() :: :ok
```

Detaches the capture handler.

---

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