Form endpoint

Every project has a form endpoint: a URL that accepts POST requests from any origin and drops each submission into your project's inbox. Point a plain HTML form at it, or call it with fetch: no backend, no email service, no API key.

Your endpoint looks like this, with your own form id at the end:

endpoint
POST https://mailcontact.app/api/forms/your-form-id

It accepts both FormData (what a native HTML form sends) and application/json. Each accepted submission lands in your inbox and triggers your notification channels.

Fields

fieldrequirednotes
nameoptionalShown as the sender. Falls back to the email if empty. Truncated at 200 characters.
emailrequiredThe sender's email. Validated: invalid or missing addresses are rejected with a 400.
subjectoptionalTruncated at 300 characters.
messagerequiredThe message body. Must be non-empty. Truncated at 5,000 characters.

One special field: a non-empty _gotcha marks the submission as bot traffic and it is silently dropped (that's the honeypot spam trick). Every other unknown field is ignored.

Plain HTML form

The zero-JavaScript version. Name your inputs after the fields above and the browser does the rest:

contact.html
<form action="https://mailcontact.app/api/forms/your-form-id" method="POST">
  <input type="text" name="name" placeholder="Your name" />
  <input type="email" name="email" placeholder="you@email.com" required />
  <input type="text" name="subject" placeholder="Subject" />
  <textarea name="message" placeholder="Your message" required></textarea>
  <button type="submit">Send</button>
</form>

One caveat in the beta: there's no redirect after a native form post yet, so the browser navigates to the endpoint's JSON response. A configurable redirect back to your site is coming with the launch. Until then, submit with fetch (below) to keep visitors on the page and show an inline success state.

JSON with fetch

Send application/json with a Content-Type header. Same fields, same validation:

submit.js
const res = await fetch("https://mailcontact.app/api/forms/your-form-id", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    name: "Ada",
    email: "ada@example.com",
    subject: "Hello",
    message: "Trying out the endpoint.",
  }),
});

const data = await res.json();
if (data.ok) {
  // show your inline "thanks, message sent" state
}

You can also POST a FormData object from JavaScript: skip the Content-Type header and pass new FormData(formElement) as the body.

CORS

CORS is open: the endpoint answers with Access-Control-Allow-Origin: * and handles the OPTIONS preflight, so browser submissions work from any origin: your domain, localhost, a staging URL, or a static host like GitHub Pages. Nothing to allowlist.

Responses and errors

On success the endpoint returns 200 with {"ok": true}. Every error is JSON too, shaped {"ok": false, "error": "..."}:

statuserrorwhen
400A valid email is required.email is missing, invalid, or longer than 320 characters
400Message cannot be empty.message is missing or blank
404Unknown form.the form id in the URL doesn't exist
413Payload too large.request body over 64 KB
429Too many requests.more than 20 requests per minute from one IP

Limits

  • Request bodies are capped at 64 KB.
  • Rate limit: 20 requests per minute per IP, answered with a 429 beyond that.
  • Field lengths: name 200, subject 300, message 5,000 characters (longer values are truncated); emails over 320 characters are rejected.

These are abuse controls, not a quota system, and they're generous for a contact form. If you expect real volume, that's Pro-plan territory; see pricing.

Related

New here? Start with Getting started. Your project also has a real email address feeding the same inbox, and Spam protection covers keeping junk out of an open endpoint. For a ready-made snippet, try the contact form generator or the framework guides.