Submit form to email
How a browser or a small script can submit form to email: access keys, JSON replies, redirects, and the fields that must stay out of the message.
People search for a way to submit form to email because the form already exists. The fields are named. The button works. The missing piece is a URL that accepts the POST and turns it into a message. This note is that URL, and the rules around it.

The request
Send a POST to https://getformpost.com/api/submit. A browser can submit form to email with no script at all. The body can be application/x-www-form-urlencoded, multipart/form-data, or a JSON object whose values are strings, numbers, or booleans. Arrays and nested objects are skipped. Files are skipped. The body must stay under the size cap, and you need at least one visible field besides the key.
The HTML below updates as you type. You do not edit the code.
HTML
<form action="https://getformpost.com/api/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="hidden" name="_subject" value="New project inquiry" />
<input type="email" name="email" required />
<textarea name="message" required></textarea>
<button>Send</button>
</form>access_key selects the inbox. _subject sets the subject line, with newlines removed and the length capped. email (or from) becomes the reply-to when it is one plain address. A value with a line break is ignored so a field cannot add extra headers.
A form to send email is the same request. The words differ in the search box. The POST does not. Use one page, one endpoint, and one key.
JSON clients
A fetch call should ask for JSON when a script needs to submit form to email and stay on the page. Set the accept header, or send _reply=json, or post Content-Type: application/json. Any one of those returns { "success": true, "message": "...", "data": { "id", "email" } } and does not redirect. email is sent, logged, or failed. Logged means delivery is not connected yet, so the message stays in the submission list. Failed means the row was saved and delivery returned an error. The dashboard lists both.

The HTML below updates as you type. You do not edit the code.
JavaScript
await fetch('https://getformpost.com/api/submit', {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json' },
body: JSON.stringify({
access_key: 'YOUR_ACCESS_KEY',
email: 'ada@example.com',
message: 'Hello',
_reply: 'json',
}),
});Do not put the access key in a query string. Keep it in the body. Do not log the full body in your own analytics if the message might contain a phone number or a private detail. The endpoint stores it because you asked it to. Your analytics script does not need a second copy.
Redirects

A classic form, with no JSON accept header, can submit form to email and then follow a thank-you URL when you have saved one on the inbox. The saved value must be https, or http on localhost. The POST itself cannot supply a different address. A hidden redirect input is ignored. That stops a copied form from sending the visitor to a site you do not own.
If you have not saved a URL, the visitor sees a short confirmation page with a link back to the website contact form. Use a real thank-you page on your own host when the form sits on a marketing site. Thank them, tell them when you reply, and do not ask them to submit again.
Limits you will hit on purpose
The honeypot fields are botcheck, _honey, and _gotcha. If any of them is non-empty, the response says success and nothing is stored or mailed. Leave one in the form, hidden, and do not give it a label a person would fill.

The same IP, and the same key, must wait a few seconds between posts. A second immediate attempt to submit form to email returns 429. Wait and try again. Past the free monthly cap the post is stored locked, with no field email. Pro past its cap returns 429 and stores nothing. The free plan stores 100 submissions a month. See the free contact form page. A paid subscription raises the cap. The price is the catalog price, not a number in the form.
An unknown key returns 400. A body with only the key returns 400, because there is nothing to mail. A honeypot hit returns 200. Treat 200 as "the visitor is done," and treat the dashboard as "the message exists."
What the mail contains

The message you read after you submit form to email lists each public field as name: value. The access key is not included. The honeypot is not included. You can reply from your mail app and the reply goes to the visitor when they typed a real address. If they left the email blank, you still have the other fields in the dashboard, and you have no one to answer.
The form to email page describes the product in one sitting. The html contact form send to email guide is the copy-paste version of the same request. This page is the contract: which headers, which status codes, and which fields are reserved.
Use the endpoint from a static generator, a hand-written page, or a few lines of fetch. Do not build a second backend that forwards into it unless you have a reason to hide the key. Hiding the key in a server you operate is reasonable. Hiding it in a client bundle is not hiding. The key is already public once the page ships.
When a test should submit form to email, use a key from a test inbox and point the notify address at a mailbox you control. The public demo key accepts a post and keeps it in the submission list. It does not deliver to your private address, which is the point of a demo anyone can press.
After the short wait, another tab can send again with the same key. Do not let a scheduled job submit form to email in a tight loop. Link here from your own docs instead of inventing another name for the same POST.