AI Build South Africa
Checked against the SPF, DKIM and DMARC standards, Gmail's sender guidelines and the text of POPIA, September 2026.

Quick answer

Your app can't send email from the browser. Sending needs a secret key for an email service, and anything in the browser is public. So a form submission, or the payment provider's notification, calls a small backend function, the function reads the key from a stored secret, and the email service sends the message from an address at your own domain.

To stay out of spam, add the SPF, DKIM and DMARC records your email service gives you, so inboxes can check the email is really yours. Send contact-form emails only to yourself, send receipts only after the payment is confirmed, and never let a failed email cancel an order. Under POPIA, the email service handles your customers' details for you, so read its terms and know where it stores them. The prompt at the end gets your AI to plan all of this before it writes code.

What transactional email is, and what it isn't

A transactional email is sent to one person because of something they just did: they filled in your contact form, paid for an order, signed up or asked to reset a password. It's expected, it's about that one action, and it usually has to arrive within a minute or two to be any use.

A marketing email is different. A newsletter, a promotion or a "we've missed you" message goes to many people because you chose to send it, not because they just did something. The difference matters twice over: email services and inboxes treat the two differently, and South African law treats them differently too (the POPIA section below covers that). This guide is about the first kind.

Why your app can't send email from the browser

Everything in your site's frontend runs in the visitor's browser, and anyone can open the files it downloads. To send an email automatically, your app has to log in to an email service with a secret key. Put that key in the frontend and every visitor can copy it and send email as you. Our guide to why an API key can't go in frontend code explains what happens after that.

The other shortcut, a mailto: link, doesn't send anything. It opens the visitor's own email app with a draft, if they have one set up, and leaves them to press send. That's fine for a "write to us" link. It can't send a receipt or tell you a form was filled in.

So every working setup has the same shape, whichever AI website builder you use:

  1. Something happens: a form is submitted, or a payment is confirmed.
  2. Your backend (a small function running on a server, not in the browser) is called.
  3. The function reads the email service's key from a stored secret, never from the code.
  4. It asks the email service to send one message, from an address at your own domain.
  5. The email service delivers it, and the receiving inbox checks that it really came from you.

Some builders include a built-in way to send email that does steps 2 to 4 for you. These usually come with limits, such as who you can send to or whether you can attach files, so check your builder's own documentation for what its version allows. If you connect an outside email service instead, your AI coding agent writes the function and you store the key as a secret. Our guide to environment variables that stop working after deploy covers the most common reason that key goes missing on the live site.

Three common emails, and where each is triggered

What sets each email off, and the mistake to avoid
Email Sent when Watch for
Contact-form notification The form is submitted and your backend function receives it. It goes to your own inbox. The function must send to your address only, never to an address the form supplies.
Receipt or order confirmation Your server has confirmed the payment through the payment provider's signed notification. Not when the customer lands on your "thank you" page. That page can be opened without paying.
Sign-up or password reset Someone signs up or asks for a reset. The service that handles your app's sign-ins usually sends these itself. Check before asking your AI to build your own.

The receipt row is the one AI-built apps most often get wrong. Our guide to adding a payment gateway explains why only the provider's signed notification proves a payment went through, and our plain-English guide to webhooks explains what that notification is.

Why emails land in spam: proving the email is really from you

Anyone can type any "From" address into an email. So inboxes check whether a message claiming to be from your domain really is. Three records in your domain's DNS settings (the public list of settings for your domain) answer that check. Your email service tells you exactly what to add; what matters is knowing what each one does.

The three records that prove an email is yours
Record What it says Without it
SPF Which servers are allowed to send email for your domain. The inbox can't tell your email service's server from a forger's.
DKIM A digital signature on each message, checked against a public key in your DNS. Nothing proves your domain stands behind the message.
DMARC What an inbox should do with mail that fails those checks, and where to send reports. Inboxes decide for themselves, and you never hear about forgeries.

These aren't one company's rules. They're open standards: the SPF standard describes how domain owners can "explicitly authorize the hosts that are allowed to use their domain names", and the DKIM standard describes a signature that lets "the signing domain ... claim some responsibility for a message". DMARC ties them to the address the reader actually sees: a message passes when SPF or DKIM passes for a domain that matches the one in the "From" line.

Big inboxes now expect them. Gmail's sender guidelines, for one, tell every sender to "Set up SPF or DKIM email authentication for your sending domains", and ask bulk senders for DMARC as well. Two practical results follow:

  • Send from an address at your own domain, such as orders@yourbusiness.co.za. You can only add these records to a domain you control, so a free @gmail.com address can't pass these checks for your app. Don't paste your personal mailbox's password into the app to send through it either: that hands your whole mailbox to the app's code.
  • Do it before launch. Records can take time to be seen everywhere, and many email services won't send to other people's addresses until your domain checks out. Our guide to what DNS is explains these records, and our guide to connecting your own domain covers where you add them.

Keeping your contact form from being misused

A backend function that sends email has a public web address (a URL), because your form has to be able to call it. Anyone who finds that URL can call it too. Three rules keep it safe:

  • Fix the recipient in the function. A contact-form function should only ever email you. If it sends to whatever address it's handed, strangers can use your domain to send their own email, and your domain's reputation pays for it.
  • Put the visitor's address in "Reply-to", not "From". The message comes from your domain, so it passes the checks above, and pressing reply still reaches the visitor.
  • Slow down the bots. Forms attract automated junk. A spam check on the form (a hidden field, a challenge, or a limit on how often one visitor can send) stops your inbox and your sending allowance filling up with it.

If the form calls the function straight from the browser, the function also has to answer the browser's CORS check, a security check that browsers enforce on calls to another web address. Our CORS error guide explains it. When no browser is involved, as when a payment provider calls your function directly, there's no CORS check to answer.

When sending fails

Email services have outages, keys expire, and an address can be mistyped. Decide in advance what happens then:

  • A failed email never undoes the real action. An order that went through but whose receipt didn't send is a small problem you can fix by hand. An order that gets cancelled because its receipt failed is a much bigger one, caused by your own code.
  • Every failure is logged, somewhere you'll actually see it, with the reason the email service gave.
  • A retry never sends twice. If the function tries again, it should check first whether that email already went out. Payment notifications can arrive more than once, so the receipt function needs this check anyway.

POPIA: your email service handles your customers' details

An email address is personal information under POPIA; the Act's definition lists "e-mail address" by name. So are the name and message someone types into your form. Sending that through an email service means the service processes it for you. In the Act's words, you're the "responsible party" and the service is an "operator": a person who "processes personal information for a responsible party in terms of a contract or mandate". Four things follow:

  • A written contract. Section 21 requires "a written contract between the responsible party and the operator" that makes the operator keep the information secure. With an email service, that's usually its terms of service and a data processing agreement. Read them, and keep a copy.
  • Where the data goes. Check where your email service's servers are. If they're outside South Africa, section 72 applies. It only allows personal information to go to a third party in a foreign country on one of the grounds it lists. Among them: the recipient is bound by a law or a "binding agreement" giving adequate protection, the person consents, or the transfer is needed to perform a contract with them. Check which one covers you, and say so in your privacy notice.
  • Only what you need. Don't send the email service more than the message needs. A receipt needs the order and the amount, not the customer's full profile.
  • Receipts and newsletters follow different rules. Section 69 prohibits direct marketing by email unless the person "has given his, her or its consent" or is an existing customer, and for customers only about your "own similar products or services", with a chance to object in every message. A receipt isn't marketing. Adding a promotion to it can make it marketing.

This is general information, not legal advice. Our guide to what POPIA asks of your app's backend covers the rest of what stays your job.

How to tell what's wrong

Your email service keeps a log of every message it was asked to send, and your backend keeps its own logs. Between them, they usually tell you where the email stopped.

What you see, and where to look first
What you see Likely cause Check first
Form submits, nothing arrives The function was never called, or it failed before sending The browser console (the error panel in your browser's developer tools), then the backend's logs
Works in preview, not live The email service's key isn't set for the live site The live site's secrets
Only arrives at your own address The sending domain isn't verified yet, so the service limits who it sends to Domain status at the email service
Arrives in spam SPF, DKIM or DMARC missing or failing, or a "From" address not on your domain The DNS records and the "From" address
Customer got it twice A retry, or a repeated payment notification, with no "already sent" check The email service's log
Order failed when email failed The email error stopped the rest of the function The function's error handling

What isn't possible

  • No setting guarantees the inbox. SPF, DKIM and DMARC prove an email is yours. They don't force Gmail or Outlook to put it in the inbox. Content, sending volume and complaints still count.
  • The browser can't send it safely. There's no frontend-only way to send automatic email with a secret key without publishing that key. Tools that let a browser send email directly only work by fixing what can be sent, such as set templates, and still can't hold a secret key.
  • Your AI can't check your DNS or your inbox. It can write the function and tell you which kinds of records you'll need. Adding them, and confirming the service shows the domain as verified, is your job.
  • A "sent" status isn't a delivered one. The email service accepting a message only means it will try. Its log can show whether the receiving inbox accepted it or bounced it.

The prompt to give your AI

Copy this into your AI coding agent's chat. It asks for a plan before any code, so you can check the plan against this guide first.

I'm not a coder. I want my app to send [a contact-form
notification to me / an order confirmation to the
customer]. Don't write any code yet. First give me a
plan that covers:

1. Where the email is sent from. It must be a backend
   function, not the browser. Name the file.
2. What triggers it. For a receipt, it must be the
   payment provider's verified notification, not the
   thank-you page.
3. Where the email service's key is stored, and confirm
   it is never in frontend code.
4. Who it can send to. A contact-form email goes only to
   my own address, with the visitor's address as
   Reply-to.
5. The From address, on my own domain, and the DNS
   records (SPF, DKIM, DMARC) I need to add. I'll add
   them myself.
6. What happens if sending fails: the order or form
   still succeeds, the failure is logged, and a retry
   can't send the same email twice.
7. What personal information the email service receives,
   and whether all of it is needed.

If you're not sure about something, say so.

What to check in the AI's answer

  • The sending happens in a backend function. If the AI's plan sends from a page, a component or anything the browser loads, ask it to move the work to the backend.
  • The key is read from a secret. Ask it to show the line that reads the key. The key itself should never appear in the code.
  • The receipt waits for the verified payment. If it triggers the email from the thank-you page or a redirect, ask again.
  • The contact-form recipient is fixed. Look for your address written into the function, and the visitor's address only in Reply-to.
  • It listed DNS records without claiming to add them. Compare its list with what your email service shows you. The service's own list wins.
  • A failure can't cancel the order. Ask it what the customer sees if the email service is down. The answer should be "the order still goes through".

If you'd rather start from what you want in plain words, the Custom Prompt Builder turns "send an order confirmation" into a prompt your AI can act on.

Where to go next

Not affiliated with the platforms we write about

AI Build South Africa is an independent resource. We're not endorsed by, or an official partner of, Google, Gmail or any email service. This guide is general information, not legal advice. See our Disclaimer for the full disclosure and our Terms of Service for the wider terms.

Frequently asked questions

What is a transactional email?

It's an email sent to one person because of something they just did: a contact-form notification, a receipt, an order confirmation or a password reset. It's expected, it's about that one action, and it needs to arrive quickly. A newsletter or promotion you choose to send to many people is marketing email instead, and inboxes, email services and the law all treat it differently.

What's the difference between transactional and marketing email?

A transactional email is triggered by the reader's own action and is about that action, like a receipt. A marketing email is one you decide to send to promote something, like a newsletter. In South Africa, POPIA section 69 only allows direct marketing by email with the person's consent, or to an existing customer about your own similar products or services, with a chance to object in every message. A receipt isn't marketing, but adding a promotion to it can make it marketing.

Why is my contact form not sending email?

Usually because nothing is actually sending it. A form needs a backend function that receives the submission and asks an email service to send it, using a secret key the browser never sees. Check the browser console (the error panel in your browser's developer tools) and your backend's logs first. If it works in the preview but not on the live site, the email service's key probably isn't set for the live site.

Why are my app's emails going to spam?

Most often because the inbox can't prove the email came from you. Send from an address at your own domain, and add the SPF, DKIM and DMARC records your email service gives you to your domain's DNS. Never put the visitor's address in the From line; use Reply-to for that. Even then, no setting guarantees the inbox: content, volume and complaints still count.

Do I need both SPF and DKIM?

Set up both. SPF says which servers may send for your domain, and DKIM adds a signature to each message that's checked against a key in your DNS. Gmail's sender guidelines ask every sender for at least one of them, and DMARC, which Gmail asks bulk senders for, passes when either one passes for the domain in your From line, so having both means one breaking doesn't fail the message. Your email service gives you the exact records to add.

Can I send email straight from JavaScript or a static website?

Not safely with a secret key. Anything in a static site or frontend JavaScript runs in the visitor's browser, where anyone can read it, so a key there is published. A mailto link only opens the visitor's own email app. You need a small backend function, which your AI coding agent can usually write for you, or a form service that sends on your behalf.

Related reading