The lead came in on a Saturday. Tally form, three fields — name, budget, what they wanted built. The email notification fired exactly the way it was supposed to and landed in a personal Gmail account I'd wired up eight months earlier and then completely forgotten about.
I found it Tuesday. Under a Costco receipt and two newsletter digests.
Not Tally's fault. The form did its job. I'd pointed it at an inbox nobody watched, and then I made the second mistake, which was worse: I "fixed" it by rebuilding the notification to send from the respondent's address so replies would just work. That version failed SPF on about a third of sends and quietly went to spam for the rest.
We're the JustEmails team, part of Velocity Digital Labs — the same group that builds ClickzProtect for bot detection and JustAnalytics for privacy-first analytics. We've written up Zapier and Baserow and NocoDB already. Form tools deserve their own walkthrough, because routing Typeform email notifications to a custom domain has its own failure mode, and almost everyone hits it.
What You'll Have at the End
Submissions landing in a mailbox on your own domain — leads@yourcompany.com, not yourname+forms@gmail.com — with the respondent's address in Reply-To so hitting reply does the obvious thing.
Two paths. You can run both on the same form, and most people probably should: the five-minute path that uses each tool's own notification settings, and the longer one that routes the webhook through the JustEmails API so the message actually originates from your domain, carrying your SPF and DKIM. Do the first today. Add the second the week it starts bothering you.
Before You Start
- A JustEmails account with a verified domain. The 7-day trial is enough to test this end to end — card required, not charged until it ends.
- A Typeform or Tally form that's already collecting responses. Doesn't need to be live.
- For Part 3 only: somewhere to run about 30 lines of JavaScript (Cloudflare Worker, Vercel function, n8n — anything that can receive a POST and make one), plus your API key from Dashboard → API → Keys.
- Twenty minutes for the built-in path, closer to forty-five for the webhook.
Mailboxes are the cheap part. JustEmails is $49/year flat with unlimited email accounts across unlimited domains, so leads@, support@, and forms@ cost nothing extra. Make one per form if you want. I do — I'm up to fourteen, which is a filing problem I've decided is future-me's responsibility.
Typeform splits this into two things that people constantly conflate: self-notifications (Typeform emails you when someone submits) and respondent notifications (Typeform emails the person who filled it out). Different settings pages, different gotchas.
Step 1: Create the destination mailbox
In JustEmails: Domains → your domain → Email Accounts. Create leads@yourdomain.com. Or point it at an alias that forwards into an inbox you already read — our catch-all setup guide covers that, and it's the better move if you spin up forms faster than inboxes.
Step 2: Wire the self-notification
Open your form → Settings → Notifications → Self notifications. Toggle it on.
In the "Send to" field, enter leads@yourdomain.com.
For the subject, use a question reference rather than a static string. Typeform lets you insert answers with the @ mention picker — something like New lead: @{name} — @{budget} means you can triage from the notification list without opening anything. Small thing. Saves real time once you're getting a dozen a day.
Step 3: Set Reply-To to the respondent's email field
This is the step people skip, and it's the whole point.
Still in the self-notification panel, find Reply-to. Set it to your form's email question. Now when the notification hits leads@yourdomain.com and you hit reply in your mail client, it goes to the person who submitted — not to Typeform.
Note what we did not do: we didn't put the respondent's address in the From field. Typeform doesn't let you, and that restriction is protecting you. More on why in a second.
Step 4: Respondent notifications
Settings → Notifications → Respondent notifications. Toggle on, pick the email question that identifies them, set the sender name to your company, point Reply-To at leads@yourdomain.com.
The From address stays on Typeform's infrastructure. As of September 2026 custom sender domains sit behind their paid tiers, in that same panel — check what your plan actually shows instead of assuming. If that From address matters to you, skip to Part 3.
Why "Send As the Respondent" Breaks SPF
Here's the trap, and I fell straight into it.
You want replies to work, so you set the From address to whatever the respondent typed. jane@gmail.com fills out your form, the notification arrives From: jane@gmail.com, you hit reply, everything's clean. Except your mail server just told the receiving server that it was authorized to send on behalf of gmail.com.
It isn't. SPF is a DNS record on the sending domain listing which servers may send for it. Gmail's record does not include your IP, so the check returns fail. DKIM can't save you either — you'd need a private key for a domain you don't control. And since neither passes, DMARC has nothing to align, so it fails too.
What happens next depends entirely on the respondent's domain policy, which is why this bug is so maddening to debug:
| Respondent domain policy | What the receiver does |
|---|
p=none | Delivers, usually to spam. Looks like it "sometimes works" |
p=quarantine | Spam folder, reliably |
p=reject | Bounced outright. You never see the lead |
So your form appears to work for gmail.com submissions and silently drops the ones from corporate domains with strict policies. Which are, of course, the leads worth having.
The fix is one line of config: From is always your domain. Reply-To is the respondent. Reply-To carries no authentication weight — it's a courtesy header the mail client reads. Nobody checks it against DNS. This is also why no-reply addresses are a mistake; you're throwing away the one header that makes conversations work.
If you haven't set a DMARC policy on your own domain yet, our staged rollout guide walks through going from p=none to p=reject without nuking your own mail. JustEmails auto-generates SPF, DKIM, DMARC, and MTA-STS records when you add a domain, so the starting point is already correct.
Part 2: Tally's Notification Setup
Simpler, mostly because Tally has fewer knobs. Which I've stopped treating as a shortcoming.
Open your form → Integrations → Email notifications (it's also reachable from the settings sidebar under "Emails," which confused me the first time — same feature, two doors). Set Send to to leads@yourdomain.com, use @ field references in the subject the same way Typeform does, and pick your email field in the Reply to dropdown.
Three fields. That's the whole setup.
For confirmations to the respondent, use Respondent emails in the same panel. Same rule — sender name yours, Reply-To your mailbox, From stays on Tally's domain.
Tally puts webhooks on its free tier, which almost nobody does, and it's why Part 3 is easier to justify here than on Typeform. Mildly unpopular opinion: for internal forms and anything under a few hundred responses a month, Typeform's polish isn't buying you much that Tally doesn't already hand you for nothing.
Part 3: The Webhook Route
Neither tool can POST an arbitrary JSON body to an arbitrary API. Both send their payload shape to a URL you give them. So you need something small in the middle — a function that takes their shape, digs out the three or four answers you actually care about, and emits ours. Thirty lines. Fewer if your form is simple.
Step 1: Get an API key
JustEmails Dashboard → API → Keys → Create New Key. Name it "Form notifications."
Copy it right away — it's shown once. Format is je_live_xxxxxxxxxxxx. The je_test_ variant exists and doesn't deliver anywhere, which is useful until you forget you're using it and spend twenty minutes wondering where your emails went.
Step 2: Know the payload shapes
They're different, and this is where the time goes.
Tally sends a flat-ish structure — data.fields is an array of {key, label, type, value} objects. You match on label, which is the human-readable question text.
Typeform sends form_response.answers, an array where each entry has a field object with id, type, and ref, plus a typed value key that changes based on question type (email, text, choice, number). You match on field.ref — and you should go set meaningful refs in the form editor before you write any code, because the auto-generated ones are UUIDs and your matching logic becomes unreadable.
Step 3: The Worker
A Cloudflare Worker, handling Tally:
export default {
async fetch(request, env) {
const payload = await request.json();
const fields = payload.data.fields;
const get = (label) =>
fields.find((f) => f.label === label)?.value ?? "";
const name = get("Your name");
const email = get("Email");
const budget = get("Budget");
const res = await fetch("https://api.justemails.app/v1/email/send", {
method: "POST",
headers: {
Authorization: `Bearer ${env.JUSTEMAILS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
to: "leads@yourdomain.com",
from: "forms@yourdomain.com",
fromName: "Website Forms",
replyTo: email,
subject: `New lead: ${name} (${budget})`,
html: `<p><strong>${name}</strong> — ${email}</p>
<p>Budget: ${budget}</p>`,
idempotencyKey: payload.data.submissionId,
}),
});
return new Response(null, { status: res.ok ? 200 : 500 });
},
};
Two things to look at twice. replyTo carries the respondent's address while from stays on your verified domain — that's the SPF section above, in code. And idempotencyKey set to Tally's submissionId means a webhook retry can't double-send. Tally retries on non-2xx. Without the key you'd get duplicates every time your Worker had a bad afternoon.
For Typeform, swap the extraction:
const answers = payload.form_response.answers;
const get = (ref) => {
const a = answers.find((x) => x.field.ref === ref);
if (!a) return "";
return a.email ?? a.text ?? a.choice?.label ?? a.number ?? "";
};
(That ?? chain is ugly and I know it. Typeform's typed-value design is defensible — it's just annoying at the call site. If you only have three question types, hardcode them and move on.)
Step 4: Connect and test
Typeform: Connect → Webhooks → Add webhook → paste your Worker URL. There's a "Test webhook" button. Use it. Then submit a real response, because the test payload uses their sample form definition and your ref matching won't hit.
Tally: Integrations → Webhooks → paste the URL → save. Submit a real response.
Check the JustEmails dashboard under message logs if nothing arrives. The log tells you whether we accepted the message, which splits "your Worker never called us" from "we sent it and something downstream ate it." I wish I'd checked there before the evening I spent salting a Worker with console.log calls that was, it turned out, never getting called at all.
Common Errors and Fixes
401 Unauthorized
The Bearer prefix needs the space after it. Check you're on je_live_ not je_test_. And check for a trailing newline if you pasted the key into a secrets UI — that one cost me an afternoon.
422 Unprocessable Entity: from address not verified
The domain in your from field isn't verified in JustEmails. Dashboard → Domains, add it, set the DNS records. Usually verifies in under five minutes. Our custom domain setup guide has the full walkthrough.
Subject line reads New lead: ()
Your label or ref matching returned empty strings. Log the raw payload and compare — Tally labels include the exact question text, punctuation and all, so "Email" won't match a question titled "Email address".
Notifications arrive but replies go to Typeform or Tally
Reply-To isn't set. Go back to the notification panel and pick the email question from the dropdown. In the API route, confirm replyTo is actually in your JSON body and not sitting in a variable you never used.
Duplicate emails for one submission
Webhook retries. Set idempotencyKey to the submission ID and return a 2xx from your endpoint even when downstream work fails — then handle the failure out of band.
Next Steps
Fan out to multiple recipients with two API calls from the same Worker — an internal alert to leads@, a confirmation to the respondent, different HTML on each. You've already parsed the payload, so it's nearly free.
Building HTML inline gets miserable past about ten lines. Move it into a template function, or push the whole thing through Zapier if you'd rather not maintain code. Still weighing the API against plain SMTP? Our SMTP vs API breakdown covers it — for webhook-triggered work like this the API wins, mostly because SMTP hands you no message ID to correlate against when a lead goes missing and you're trying to prove it ever left.
Watch your volume in Dashboard → API → Usage. The base plan's 1,000/month covers most contact forms. Add 10,000/month for $25/year when it doesn't.
One more thing, and I'm serious about this one: go submit your own form. From your phone, on cellular, not from the office network. Most of the broken form notifications I've run into were broken from the day someone set them up — nobody caught it because nobody ever filled the thing out from the outside.
Frequently Asked Questions
Can Typeform send form notifications from my own custom domain?
Not by default. Typeform's built-in self-notifications and respondent notifications go out from Typeform's own sending infrastructure — you control the sender name and the Reply-To, not the envelope domain. Some paid tiers have offered a custom sender-domain option, so check your plan's notification settings rather than trusting a blog post. If you need every outbound message to carry your domain in the From header with your own SPF and DKIM, use the webhook route: Typeform POSTs the response to your endpoint, and your endpoint calls the JustEmails API.
Because SPF checks the sending server against the DNS record of the domain in the envelope sender. If a respondent types jane@gmail.com and you put that in the From field, your mail server is now claiming to be gmail.com — and gmail.com's SPF record does not list your IP. SPF fails, DKIM can't align with a domain you don't hold a key for, so DMARC fails too. Put your own domain in From and the respondent's address in Reply-To. Replies still go to the right person and authentication passes.
Do I need the webhook route, or is the built-in notification enough?
The built-in notification is enough if you just want submissions landing in a mailbox a human reads. It takes about five minutes and there's no code. Go the webhook route when you need the message to come from your domain, when you need to fan one submission out to several addresses with different content, when you want retries on failure, or when you want the message body to include computed values the form itself never collected.
The base $49/year plan includes 1,000 transactional API emails per month. A contact form doing 20 submissions a day with a confirmation to the respondent plus an internal alert uses roughly 1,200 — just over the line. If you need more, add 10,000 emails/month for $25/year, and the add-on stacks as many times as you need.
Try JustEmails
Unlimited custom domain email hosting for $49/year flat — unlimited domains, unlimited mailboxes, 10 GB storage, full IMAP/SMTP. Built for agencies, freelancers, and anyone managing email across more than one domain.
Start your 7-day free trial → · How it compares