# Elixir

Req in a small module, supervised the way a Phoenix app expects.

There is no emails.sh hex package. Req is the HTTP client most Elixir projects reach for, and the module below is the whole integration.

### Dependency

mix.exs, then mix deps.get:
```text
# mix.exs
defp deps do
  [
    {:req, "~> 0.5"}
  ]
end
```

### Send

lib/acme/email.ex:
```text
# lib/acme/email.ex
defmodule Acme.Email do
  @moduledoc """
  Transactional email over emails.sh.

  The key comes from https://emails.sh/dashboard/api-keys and is read from the
  EMAILSSH_API_KEY environment variable at runtime, never compiled in.
  """

  @endpoint "https://emails.sh/v1/emails"

  @spec send(keyword()) :: {:ok, String.t()} | {:error, String.t()}
  def send(opts) do
    body = %{
      from: Keyword.get(opts, :from, "Acme <hello@acme.com>"),
      to: [Keyword.fetch!(opts, :to)],
      subject: Keyword.fetch!(opts, :subject),
      html: Keyword.fetch!(opts, :html)
    }

    case Req.post(@endpoint, json: body, auth: {:bearer, key()}, receive_timeout: 10_000) do
      {:ok, %{status: status, body: %{"id" => id}}} when status < 400 ->
        {:ok, id}

      {:ok, %{body: %{"error" => %{"code" => code, "message" => message} = refusal}}} ->
        # next says what to do about it, so keep it rather than the code alone.
        {:error, "#{code}: #{message} #{refusal["next"]}"}

      {:error, reason} ->
        {:error, "emails.sh unreachable: #{inspect(reason)}"}
    end
  end

  defp key do
    System.fetch_env!("EMAILSSH_API_KEY")
  end
end
```

In iex:
```text
iex> Acme.Email.send(to: "ada@example.com", subject: "Your receipt from Acme", html: "<p>Thanks.</p>")
{:ok, "em_01J9X8Q2K7Y4RN3M"}
```

### Off the request path

A send takes tens of milliseconds, but it is still a network call in front of a user. Task.Supervisor keeps it off the response, and a crash in the task does not take the caller with it.

Supervised, fire and forget:
```text
# In your application supervision tree
children = [
  {Task.Supervisor, name: Acme.TaskSupervisor}
]

# At the call site
Task.Supervisor.start_child(Acme.TaskSupervisor, fn ->
  Acme.Email.send(
    to: user.email,
    subject: "Welcome to Acme",
    html: "<p>Confirm your address to finish signing up.</p>"
  )
end)
```

In Phoenix, wire it as a Swoosh adapter or call it from a context: /docs/phoenix.

---

Base URL: https://emails.sh/v1. Auth: `Authorization: Bearer esh_...`.
Whole API in one file: https://emails.sh/llms.txt. All documentation: https://emails.sh/docs.md.
