Sending data
JavaScript & AJAX
Submit forms with fetch to keep users on the page, show inline success/error messages, or build a single-page-app flow.
JSON request, JSON response
Send the form data as JSON with Content-Type: application/json. Set Accept: application/json so the endpoint always returns JSON instead of redirecting.
js
async function submit(form) {
const data = Object.fromEntries(new FormData(form));
const res = await fetch("https://formto.email/f/YOUR_FORM_ID", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify(data),
});
if (res.ok) {
form.reset();
showSuccess("Thanks — we got your message.");
return;
}
const { error } = await res.json().catch(() => ({}));
showError(error || "Something went wrong. Please try again.");
}Or just send FormData
If you'd rather not serialize, pass the FormData directly. The endpoint accepts multipart/form-data and application/x-www-form-urlencoded in addition to JSON.
js
const res = await fetch("https://formto.email/f/YOUR_FORM_ID", {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});Response shape
On success (HTTP 200):
json
{ "ok": true, "message": "Submission received" }On error (4xx / 5xx):
json
{ "error": "Submission is empty — fill in at least one field." }CORS
The endpoint sets Access-Control-Allow-Origin: * and responds to OPTIONS preflights, so you can call it from any origin without proxying.
Don't put secrets in the browser
The form ID is public by design — it's the URL anyone needs to submit to your form. There's no API key. Use the rate limits and honeypot to keep things sane, not the URL itself.
Full vanilla example
html
<form id="contact">
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required></textarea>
<button type="submit">Send</button>
<p data-status></p>
</form>
<script>
const form = document.getElementById("contact");
const status = form.querySelector("[data-status]");
form.addEventListener("submit", async (e) => {
e.preventDefault();
status.textContent = "Sending…";
const res = await fetch("https://formto.email/f/YOUR_FORM_ID", {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});
if (res.ok) {
form.reset();
status.textContent = "Thanks — we got your message.";
} else {
const { error } = await res.json().catch(() => ({}));
status.textContent = error || "Something went wrong.";
}
});
</script>