# `Letterbox`
[🔗](https://github.com/hattoriai/letterbox/blob/v0.1.0-rc.0/lib/letterbox.ex#L1)

Inbound email for Phoenix: emails to your app, kept, read and given to
your code.

    defmodule MyApp.Mailboxes do
      use Letterbox.Routing

      route "support@", to: MyApp.SupportMailbox
    end

    defmodule MyApp.SupportMailbox do
      use Letterbox.Mailbox

      @impl true
      def process(message), do: MyApp.Tickets.open(message.verified_from || message.from, message.text)
    end

A message reaches the app through an ingress (`Letterbox.Ingress`): a
provider's webhook (`Letterbox.Ingress.Resend`), a relay of raw emails
(`Letterbox.Ingress.Relay`), or the app's own code with `ingest/2`.
Letterbox keeps the raw email in the app's store, a row for it in the
app's database (`Letterbox.Inbound`), and, out of the request
(`Letterbox.Runner`), reads it (`Letterbox.Email`) and gives it to the
mailbox its routes name (`Letterbox.Routing`).

## Telemetry

  * `[:letterbox, :ingest, :start | :stop | :exception]` - a message
    kept, with `:ingress` and `:bytes`; the `:stop` has its `:result`,
    the row's status (or `:error`).
  * `[:letterbox, :process, :start | :stop | :exception]` - a mailbox
    given a message, with `:id`, `:mailbox`, `:ingress` and `:attempt`;
    the `:stop` has its `:result`: `:delivered`, `:bounced`, `:error`
    or `:exception`.

# `delete`

```elixir
@spec delete(Letterbox.Inbound.t() | String.t()) :: :ok | {:error, term()}
```

Deletes a message: its row and its raw email.

# `get`

```elixir
@spec get(String.t()) :: Letterbox.Inbound.t() | nil
```

A message's row, or nil.

# `incinerate`

```elixir
@spec incinerate(pos_integer()) :: non_neg_integer()
```

Deletes the messages done with for longer than `days` (their row and
their raw email): delivered, bounced, unrouted or failed. Gives how many.

# `ingest`

```elixir
@spec ingest(binary(), keyword()) :: {:ok, Letterbox.Inbound.t()} | {:error, term()}
```

Takes a raw email from the app's own code: kept, then given to its
mailbox out of the caller. Gives the message's row.

Options:

  * `:ingress` - a name for where it came from (`"app"`).
  * `:external_id` - its id where it came from: the same id twice is one
    message.
  * `:recipients` - the addresses the mail server took it for (its
    envelope), when they are known.
  * `:meta` - a map kept with it.
  * `:runner` - for this message only (`Letterbox.Runner`).

# `list`

```elixir
@spec list(keyword()) :: [Letterbox.Inbound.t()]
```

The messages, the newest first. Options: `:status` (one or a list),
`:mailbox` (a module), `:limit` (50) and `:offset`.

# `message`

```elixir
@spec message(Letterbox.Inbound.t() | String.t()) ::
  {:ok, Letterbox.Message.t()} | {:error, term()}
```

A message as its mailbox gets it (`Letterbox.Message`), read again from its raw email.

# `raw`

```elixir
@spec raw(Letterbox.Inbound.t() | String.t()) :: {:ok, binary()} | {:error, term()}
```

A message's raw email, as it came.

# `resume`

```elixir
@spec resume(keyword()) :: non_neg_integer()
```

Gives the runner the messages that were waiting: those to fetch from
their provider, and those for their mailbox. After a restart, with the
local runner, they are not lost (`Letterbox.Sweeper` does it on start).
Gives how many.

# `retry`

```elixir
@spec retry(Letterbox.Inbound.t() | String.t(), keyword()) ::
  {:ok, Letterbox.Inbound.t()} | {:error, term()}
```

Gives a message to its mailbox again, from its first attempt: one that
failed, bounced or was not routed, once the cause is fixed.

---

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