Better Auth calls a function of yours every time it needs to send email. The integration is that function — there is no adapter, plugin, or intermediate configuration.
This is what we do with ourselves. The Kourio dashboard authenticates with Better Auth, and its reset and verification emails leave through our own transport. This guide is our production code with the names changed.

Install

.env
The key allows sending on behalf of every domain you have verified. It lives on the server, never in code that reaches the browser.

The minimum that works

auth.ts

One sender, in one place

After the third hook, the same block starts getting copied with a different comma. Worth extracting before that:
mailer.ts
A magic link is a password. State the expiry in the message and keep it short. A sign-in link valid for a week is a permanent password forgotten in an inbox.

One-time code (OTP)

Put the code in the subject. Someone waiting for a code reads it in the notification without opening the message — and that is the difference between a few seconds and one more step in the middle of a sign-in.

Organization invitation

With a Kourio template

If you want your brand in the email without writing 2007-era HTML, publish a template in the dashboard and send only the variables. Subject and sender come from it:
Each template block is a contract: given that content, Kourio guarantees it renders correctly in Outlook, in dark mode, and at 600px wide. You write the text; the markup is our problem.

Three things that only show up in production

HTML-only mail scores worse with spam filters, and a password reset is exactly the one that cannot land in the wrong folder. The text field costs one line.
If sendResetPassword throws, Better Auth propagates the error to whoever asked — and the difference between “sent” and “error” tells an attacker which addresses exist in your database. Log the failure and answer the same either way.
Leave these sends as TRANSACTIONAL, which is the default. The class decides which reputation the message leaves under, and the two are measured apart precisely so a bad campaign cannot stop someone from getting into their own account.

When it does not arrive

1

Check that the domain is verified

No message leaves a domain without SPF, DKIM and DMARC published. See Domains.
2

Look the message up by address

GET /v1/emails?search=… brings the timeline: accepted, delivered, deferred, bounced. It is what answers “did it arrive?” without depending on your logs.
3

Check whether the address is suppressed

An address that hard-bounced stops receiving — transactional included. See Errors.

Sending

The two ways to compose a message, what counts against your quota, and why marketing takes a different path.