Letterbox (Letterbox v0.1.0-rc.0)

Copy Markdown View Source

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.

Summary

Functions

Deletes a message: its row and its raw email.

A message's row, or nil.

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

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.

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

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

A message's raw email, as it came.

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.

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

Functions

delete(id)

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

Deletes a message: its row and its raw email.

get(id)

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

A message's row, or nil.

incinerate(days)

@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(raw, opts \\ [])

@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(opts \\ [])

@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(inbound_or_id)

@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(id)

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

A message's raw email, as it came.

resume(opts \\ [])

@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(inbound_or_id, opts \\ [])

@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.