# `PhoenixKit.Modules.Emails.SendJob`
[🔗](https://github.com/BeamLabEU/phoenix_kit_emails/blob/0.4.2/lib/phoenix_kit/modules/emails/send_job.ex#L1)

Delivers one queued message.

`PhoenixKit.Modules.Emails.Queue` enqueues the serialized `Swoosh.Email`; this
worker rebuilds it and hands it back to `PhoenixKit.Mailer.deliver_email/2`
with `skip_queue: true` and `already_intercepted: true`, so the real send takes
the ordinary path — recipient blocklist, integration-or-static-mailer choice,
post-send tracking — without being offered back to the queue it just came from
and without being run through interception a second time.

The message also carries its `X-PhoenixKit-Log-Id` header across the hop, so
`handle_after_send/2` updates the row this message already has. The
interceptor's own idempotency check on that header stays as a second line of
defence for a core old enough not to know `already_intercepted`.

## Delivery is at-least-once

If the relay accepts the message but the connection drops before its reply,
the send looks like a failure, Oban retries, and the recipient gets the mail
twice (up to `max_attempts`). That is inherent to handing delivery to a queue
— it is why authentication mail is excluded by default, and why a caller that
cannot tolerate a duplicate should pass `queue: false`.

## Host setup

The `:emails` queue must exist in the **host application's** Oban config — a
package cannot add a queue to someone else's supervision tree:

    config :my_app, Oban, queues: [emails: 10, ...]

Without it the jobs simply sit in `available` forever, which is why
`Queue.status/0` is surfaced on the settings page.

---

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