Skip to content
Lovable Field Guide
Start here

Setting up Resend in a Lovable app

The Lovable Resend setup that actually ships: what the shared connector can't do, the DNS you verify first, and the deliverability work nobody documents.

11 min read

Resend in Lovable is a connector you paste an API key into, and it will send email from your app almost immediately. The part that takes real work (and the part that decides whether anyone outside your own inbox ever receives anything) is the DNS work you have to finish in Resend first, because the connector cannot do it for you.

The short version:

  • Resend is an app + chat connector: one shared connection, usable in Lovable chat during development and in your published app.
  • One Resend account is shared across every linked project. There is no per-project key.
  • You supply an API key prefixed re_, either sending access (dispatch only, optionally scoped to a single domain) or full access.
  • The connector cannot verify domains and cannot receive webhooks. Both of those happen outside Lovable.
  • Until you verify a domain, you are sending from onboarding@resend.dev, which only delivers to the Resend account owner. That is the single most common “it works for me but nobody else got it” bug.

What the connector actually is

Lovable organizes integrations by connection type rather than by category, and Resend sits in the first type: an app + chat connector with shared credentials. That means the same connection is available to the agent while you build and to your app at runtime. You can ask Lovable in chat to send a test email, and the deployed app can call the Resend API using the same stored key.

Per Lovable’s Resend documentation, generated apps can send HTML and plain-text transactional email with multiple recipients, cc, bcc and reply-to, attachments and tracking tags, and can query sending domains and email status. The limits are as important as the features:

Handled by the connector Not handled: you do this yourself
Sending Transactional sends, cc/bcc/reply-to, attachments, tags n/a
Domains Query your sending domains Adding and verifying a domain (Resend dashboard + DNS)
Events Query an email’s status Receiving webhooks (your own endpoint)
Accounts One shared connection Per-project keys, per-user Resend auth

That last row deserves a moment. The connection is shared across every project you link it to, so your staging project and your production project send through the same Resend account, with the same key, against the same domain reputation. There is no environment boundary built in.

The wider picture of how Lovable wires third-party services (six connection types, what each one can reach) is in the Lovable integrations guide.

Step 1: verify your sending domain in Resend, before you touch Lovable

Lovable’s docs put it plainly: email reaches your app’s users only from a domain you have verified in Resend. The connector cannot do DNS and cannot run verification. So do this first, in the Resend dashboard, at your DNS provider.

Three record types matter, and they do different jobs.

SPF is a TXT record listing which servers may send for your domain. A receiving server checks the envelope sender’s domain against it. SPF alone authorizes the sending infrastructure; it says nothing about the From: header a human sees.

DKIM is a public key published in DNS. Resend signs each outgoing message with the matching private key, and the receiver verifies the signature. This is the one that survives forwarding, and it is the stronger of the two signals.

DMARC is the policy that ties SPF or DKIM back to the visible From: domain, “alignment”, and tells receivers what to do when neither aligns. Without DMARC, a receiver has two passing checks and no instruction. With it, you get a policy and, more usefully at first, aggregate failure reports.

Resend generates the exact SPF and DKIM records for you when you add a domain; publish them verbatim and wait for propagation. DMARC you add yourself:

; TXT record at _dmarc.yourdomain.com
v=DMARC1; p=none; rua=mailto:dmarc-reports@yourdomain.com; pct=100

Start at p=none. It changes nothing about delivery, but it starts the reports flowing so you can see every source sending as your domain. Move to p=quarantine once the reports are clean, and to p=reject when you are confident. Going straight to p=reject on a domain you also use for a CRM, a help desk and a newsletter tool is how people take down their own invoices.

Step 2: create a key with the right scope

Resend keys come in two access levels, and Lovable accepts either. Choose deliberately:

  • Sending access: dispatch only, and optionally restricted to one domain. This is what a Lovable app needs. If the key leaks, the blast radius is “someone can send mail from one domain”, not “someone can delete my domains and read my logs”.
  • Full access: everything, including domain and key management. Only pick this if you have a concrete reason.

The value is prefixed re_. Copy it once; Resend will not show it again.

Step 3: connect it, then send

Add the Resend connector from Lovable’s connectors catalog and paste the key. After that, sending is a chat instruction. Be specific about the from address, because this is where the classic failure lives:

Send a transactional email when a new row is inserted into contact_submissions. Send it from notifications@send.mydomain.com, with both an HTML body and a plain-text alternative, and set reply-to to the submitter’s address.

Two things go wrong at this step, reliably.

First, the test address. Until you specify otherwise, sends go from the shared onboarding@resend.dev address, which only delivers to the Resend account owner. Your test works. Every real recipient gets nothing, with no error you will notice. Resend’s Lovable integration docs call this out directly, along with the fix.

Second, the redeploy. Changing the from address in chat updates your code; it does not update your live site. Lovable’s hosting publishes a snapshot, and changes after a publish do not affect the live site until you republish. Verifying a domain, changing the from address, and then testing the published app without republishing produces a confusing “I fixed it and nothing changed” loop.

Transactional or marketing: the line matters legally

Transactional email is triggered by, and about, something the recipient did: a receipt, a password reset, a booking confirmation, a shipping notice. Marketing email promotes something, regardless of how it is dressed up.

The distinction is not cosmetic. Under CAN-SPAM, commercial messages must carry a working opt-out and a valid physical postal address; transactional or relationship messages are exempt from most of those requirements but still may not use deceptive headers or subject lines. Under GDPR you need a lawful basis for marketing (in practice, consent) while a transactional message to fulfill a contract stands on different ground. Canada’s CASL is stricter still on consent.

Here is the trap: adding a promotional block to a transactional email can reclassify the whole message. A receipt with “while you’re here, check out our Pro plan” stops being purely transactional. If you want to upsell, send a separate message on a separate footing, ideally from a separate subdomain.

Mixing the streams also costs you technically. Marketing mail attracts complaints and unsubscribes; transactional mail must arrive. Share a domain between them and one bad campaign can send your password resets to spam.

Deliverability: five things that decide whether it lands

Authentication gets you admitted. These get you delivered.

  1. A real From address on a domain you control. Never send as @gmail.com or @outlook.com from Resend: you cannot sign or authorize mail for a domain you don’t control, so DKIM and SPF can never align with the visible From: address, and the major providers treat spoofed consumer domains harshly regardless of what policy those domains publish. notifications@send.yourdomain.com is fine; a monitored reply-to is better than a no-reply that silently eats replies.

  2. A plain-text alternative. Send text alongside html. HTML-only mail is a mild spam signal, it breaks in text-only clients and accessibility tooling, and writing the text version usually reveals that your HTML says less than you thought.

  3. Unsubscribe handling, wired properly. For anything even adjacent to marketing, set List-Unsubscribe and List-Unsubscribe-Post so mailbox providers can render a native one-click unsubscribe:

    List-Unsubscribe: <https://yourdomain.com/u/abc123>, <mailto:unsubscribe@yourdomain.com>
    List-Unsubscribe-Post: List-Unsubscribe=One-Click

    The endpoint must actually suppress the address, and it must work on a single POST with no login and no confirmation page. Note that Lovable’s connector documents recipients, cc/bcc, reply-to, attachments and tags: it does not document arbitrary custom headers. If you need these, send through your own server code against the Resend API rather than through the connector.

  4. Warming, if you are sending volume. A brand-new sending domain has no reputation. Ramp over days rather than blasting your whole list on day one, and send to engaged recipients first. For a low-volume transactional app this mostly takes care of itself; for anything list-shaped it does not.

  5. Bounce and complaint hygiene. Hard bounces must never be retried, and complaints must suppress the address permanently. Which brings us to the connector’s second limitation.

Webhooks, given the connector can’t receive them

Resend reports delivery, bounce, complaint and open/click events by webhook. Lovable’s connector cannot receive them. You have two honest options.

Poll for status. The connector can query an email’s status. For a low-volume app where you mainly want to know whether a specific receipt was delivered, a status lookup on demand is enough, and it costs you nothing to build. Do not turn this into a background job that polls every few seconds: deployed-app database and compute usage is billed out of the same credit pool as your AI coding, and a short-interval job keeps compute awake around the clock for no benefit. Credit discipline applies to runtime as well as to prompting.

Receive them yourself. Have the app expose a real HTTP endpoint and register that URL in Resend’s dashboard. On the TanStack Start stack (the default for projects created from 13 May 2026 onward) you need a real HTTP endpoint rather than a createServerFn server function, which is meant to be called from your own app. As of September 2026 Lovable’s docs don’t spell out how to ask for one, so describe the requirement in chat (a public POST URL a third party can hit) and verify what it actually generates. On an older React + Vite project, it is a Supabase edge function, the same shape as any other backend handler. Then:

// Sketch: check Resend's current webhook docs for the exact signing scheme
// and use their documented verification helper rather than hand-rolling HMAC.
// The secret is passed in, because where it comes from differs by stack:
//   edge function (React + Vite projects): Deno.env.get('RESEND_WEBHOOK_SECRET')
//   TanStack Start on Workers: the request-time env/bindings object handed to
//   your handler, not a global, and never `process.env` on either stack.
export async function handleResendWebhook(req: Request, secret: string) {
  const raw = await req.text()               // verify against the RAW body
  const ok = verifySignature(raw, req.headers, secret)
  if (!ok) return new Response('invalid signature', { status: 401 })

  const event = JSON.parse(raw)
  switch (event.type) {
    case 'email.bounced':
      // `data.to` is an ARRAY of recipients, not a single address.
      for (const addr of event.data.to) await suppress(addr, 'bounce')
      break
    case 'email.complained':
      for (const addr of event.data.to) await suppress(addr, 'complaint')
      break
  }
  return new Response('ok', { status: 200 })   // always 200, or it retries
}

Two payload details catch people out: the secret never lives on process.env in either runtime, and data.to is an array even when you only sent to one person. Past that, three rules regardless of provider: verify the signature against the raw request body before parsing, treat delivery as at-least-once and make the handler idempotent, and return 2xx quickly, doing real work asynchronously.

The other email pipeline nobody mentions

A Lovable app with authentication has two email systems, and the Resend connector is only one of them.

Signup confirmations, magic links and password resets are sent by the authentication service (Supabase Auth, whether that is Lovable Cloud or your own Supabase project) using its own SMTP settings and its own templates. Connecting Resend to Lovable does nothing for those. People verify a domain, fix their contact-form email, then spend an afternoon wondering why the signup confirmation still arrives from a generic address and lands in Promotions.

On your own Supabase, the fix is to configure custom SMTP in the Auth settings with Resend’s SMTP credentials and edit the templates there. On Lovable Cloud, auth is managed inside the editor rather than in a Supabase dashboard, and as of September 2026 Lovable’s docs don’t spell out a custom-SMTP setting for it, so ask in chat and verify what it actually wires up before you assume the auth stream is authenticated.

Either way: test both pipelines. A sent transactional email proves nothing about a signup confirmation.

When you don’t need an email provider at all

Everything above is the right amount of work if your app sends product email: receipts, resets, notifications to users who are not you. It is a lot of work if what you actually wanted was “somebody filled in the contact form, tell me about it”.

For that case the whole stack collapses. A hosted form backend takes the POST, stores the submission and emails you, with no API key in your app, no domain to verify and no function to redeploy. Formspree and Basin are the established names; a Tally or Typeform embed does it too, at the cost of not owning the markup. The build-it-yourself version (a table plus a handler plus Resend) is covered in the Lovable contact form guide, and it is genuinely worth doing when the form is part of a product rather than an inbox.

LinkyCal is the one we’d use here. Most people who land on this page want one thing: a contact form that tells them when someone fills it in. LinkyCal does that without an email provider at all. It filters spam, needs no DNS setup, and sends notifications by email, Slack, Telegram or WhatsApp, into Excel or Google Sheets, or to a webhook. Its workflows can also enrich each lead and send an auto-responder or a lead magnet the moment the form is submitted. The transactional email your product sends to users, such as receipts and password resets, still goes through Resend.

What to do next

  1. Check where your app is sending from right now. If the from address contains resend.dev, nobody outside your Resend account has received anything.
  2. Verify a dedicated sending subdomain in Resend and publish a DMARC record at p=none today, so reports accumulate while you build.
  3. Rotate to a sending-access key scoped to that one domain if you connected a full-access key to get started.
  4. Change the from address, then republish, then test against the published URL.
  5. Send yourself one real message and read the raw headers. If SPF and DKIM both pass and DMARC aligns, the technical half is done and everything left is content and reputation.
  6. Send a test signup through your auth flow separately. That email comes from somewhere else entirely.

Found something wrong or out of date? Lovable changes fast and we'd rather fix a guide than let it rot.

Disclosure: the team behind this guide also builds LinkyCal, mentioned above.

Frequently asked questions

Can the Lovable Resend connector verify my sending domain?
No. Lovable's Resend documentation states the connector cannot verify domains, and that email reaches your app's users only from a domain you have already verified inside Resend. Domain verification is a DNS task you complete in the Resend dashboard before you connect anything to Lovable.
Why does my Lovable app only send email to my own address?
Because it is still sending from the shared onboarding@resend.dev test address, which only delivers to the Resend account owner. Verify your own domain in Resend, then change the from address in your app and redeploy. Resend's Lovable integration docs note that changing the from address does not apply automatically.
Does each Lovable project get its own Resend account?
No. Resend is an app plus chat connector with a single shared connection across every linked project, so staging and production send through the same Resend account and the same API key. If you need per-project isolation, skip the connector and store your own key as a backend secret instead.
Can I receive Resend webhooks through Lovable?
Not through the connector. Lovable's docs list receiving webhooks as a limitation. You can still receive them by having your app expose its own HTTP endpoint, registering that URL in Resend's dashboard, and verifying the signature on every request.
Do I need DMARC to send transactional email?
SPF and DKIM are the hard requirement at the major mailbox providers, and DMARC is what tells them what to do when alignment fails. Publishing a DMARC record even at p=none gives you failure reports and a path to enforcement later, and bulk senders to Gmail and Yahoo have been required to publish one since February 2024.
Will the Resend connector send my Supabase auth emails?
No. Signup confirmations and password resets are sent by the authentication service using its own SMTP configuration and templates. That is a separate pipeline from your app's transactional sends, and configuring the Resend connector changes nothing about it.

Read next