Form to email
Send an HTML form to email, and know it arrived.
An HTML form cannot send email by itself. Point it at Heartdove and every submission is saved to an inbox, emailed to you straight away, and tracked until the email is delivered or bounces. No PHP, no server, no mail credentials in your site.
Updated 20 October 2026
Why an HTML form cannot email you on its own
A browser has no way to send email. A mailto: action only opens the visitor’s own email program, if they have one set up, and you never find out whether they sent anything. The old answer was a PHP script calling mail(), which needs a host that runs PHP and an unauthenticated sender that spam filters often distrust, and keeps no record of the message.
A form backend solves this properly. The form posts to an endpoint that stores the submission, sends the alert from a verified sending domain, and shows you what happened to that alert. That is what Heartdove does, and the only change to your page is the form’s action.
Point your form at Heartdove
Create a form in your Heartdove workspace, register the website address it will post from, and copy its public access key. Then set the action and add the hidden key to your existing form. The form is live immediately.
<form action="https://submit.heartdove.com/v1/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<label>Email <input type="email" name="email" required /></label>
<label>Message <textarea name="message" required></textarea></label>
<!-- Hidden spam trap: people never see it, bots tick it -->
<input type="checkbox" name="botcheck" tabindex="-1" autocomplete="off" style="display:none" />
<button type="submit">Send message</button>
</form>- action points at the Heartdove endpoint. Keep
method="POST". - access_key identifies the form. It is public and grants no access; protection comes from the exact origin check, rate limits and spam scoring.
- botcheck is a hidden trap. People never see it, bots tick it, and anything that ticks it is quarantined instead of alerting you.
- Every other field is yours. Name them whatever you like, up to 50 fields.
What happens after submit
Heartdove saves the message to your inbox before it tries to send anything, so a failed email never loses the enquiry. Then it queues the alert and tracks it step by step:
- CapturedSaved to your inbox and searchable
- Accepted by providerThe email was handed to the sending provider
- DeliveredThe recipient’s mail server accepted it
- BouncedShown with the reason; the address is paused so you notice
The visitor sees a plain confirmation page, or your own thank-you page if you add a redirect. The browser receives a 202, which means stored, not delivered; delivery is what the timeline is for.
HTTP/1.1 202 Accepted
{
"success": true,
"data": {
"id": "83841cff-c4f9-4901-9167-8e9fd559c990",
"status": "accepted",
"duplicate": false
}
}Send visitors to your own thank-you page
Add a hidden _redirect field with the full address of a page on one of the websites registered for the form. After a successful post the visitor lands there. Redirects to other sites are refused, so the key cannot be used to bounce people elsewhere.
<form action="https://submit.heartdove.com/v1/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="hidden" name="_redirect" value="https://www.yourdomain.com/thanks" />
<label>Name <input type="text" name="name" required /></label>
<label>Email <input type="email" name="email" required /></label>
<label>Message <textarea name="message" required></textarea></label>
<input type="checkbox" name="botcheck" tabindex="-1" autocomplete="off" style="display:none" />
<button type="submit">Send message</button>
</form>Post with JavaScript instead
If you want to stay on the page, post JSON with fetch. Send an Idempotency-Key header and reuse it when you retry, so a flaky connection never stores the same message twice.
const response = await fetch('https://submit.heartdove.com/v1/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(), // reuse it when you retry
},
body: JSON.stringify({
access_key: 'YOUR_ACCESS_KEY',
// turnstile_token: token, // only if the form uses Cloudflare Turnstile
fields: { email, message },
}),
});
const receipt = await response.json();
// 202 { success: true, data: { id, status: 'accepted', duplicate: false } }The typed client in @heartdove/forms does this for you, with adapters for React, Vue and Svelte. It ships with the Heartdove source and is not yet published to npm. See the SDK docs.
Spam, without a CAPTCHA
Every form checks that the post comes from your exact website address, limits how often each visitor can send, and scores each message using the hidden trap, link counts and your own deny rules. High scores are quarantined for review, not emailed to you. Cloudflare Turnstile can be added to a form that wants a human check, but it is never required. Read how the spam checks work.
Form-to-email forwarders and form backends
A form-to-email forwarder sends the email, and the email is your copy of the message. If it bounces or lands in a spam folder, you may not know there was an enquiry at all. Many form backends also store submissions; in a review of nine services on 19 October 2026, none showed delivery status for each message.
Heartdove is built the other way round: the inbox copy comes first, the email is a notification, and every notification shows whether it was delivered. Over your plan limit, submissions are held, visible, up to your plan’s hold capacity, and released automatically; nothing is billed as overage.
Questions
Is there a free plan?
Yes. Free includes 500 submissions a month with an instant email alert for each, 90 days of history, delivery tracking and spam quarantine. No card is needed.
Do I need a server or any backend code?
No. The browser posts the form to Heartdove. Your site can be plain HTML on any host.
Will the alert emails reach my inbox?
Alerts are sent from a verified Heartdove sending domain, and each message shows whether the receiving mail server accepted the email. What a mail provider does after accepting it is up to that provider. If an alert bounces, the message says so and the address is paused so you notice.
Can I get the submissions somewhere other than email?
Every submission is in your Heartdove inbox, searchable, with status and notes. Signed webhooks can forward each submission to your own endpoint. You cannot add one yourself yet: during early access, email support@heartdove.com and we will connect it, and Slack, Discord, Telegram and Notion the same way.
Can I attach files?
Not yet. The endpoint accepts text fields only; uploads are not accepted.
Never lose another enquiry.
Connect your first form in a few minutes. Your existing design stays exactly as it is.