Skip to content

Form endpoints

An endpoint is a URL you can point an ordinary HTML form at. Submissions land in a Minion form, where you read them in the Responses tab or export them as CSV.

The point is that your page no longer needs an email address on it. There is nothing for a scraper to pick up — just the endpoint URL, which is useless for sending spam to a person.

Submissions stored in MinionYes
Works with no JavaScriptYes
Read them in the UI, or export CSVYes
Read them from a minion (AI agent)Yes — the existing Forms API
Email notificationNot yet. Check the Responses tab, or have a minion poll it
Auto-reply to the senderNot yet
File attachmentsNot supported. A submission with a file is rejected
  1. Open the form in Minion → the Endpoints tab → Create endpoint.
  2. Fill in Allowed sites with the site the form lives on, and Redirect after submit with a page on that same site.
  3. Copy the Submit URL, or the whole embed snippet.

This is the whole integration. No JavaScript, no build step.

<form action="https://YOUR-HQ/api/public/forms/e/YOUR-TOKEN" method="POST">
<input type="text" name="name">
<input type="email" name="email">
<textarea name="message"></textarea>
<!-- Spam trap. Keep it hidden and leave it empty. -->
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
<button type="submit">Send</button>
</form>

You do not declare the fields in Minion. Whatever name attributes you use become the columns in the Responses tab. Rename them, add them, drop them — nothing to keep in sync.

_gotcha is a field a human never sees and never fills. Bots fill everything they find, so a submission with a value in it is discarded.

Note that it is not called company — on a contact form “company name” is a field real people fill in, and using it as a trap would silently throw away real enquiries.

Names starting with _ are instructions to the endpoint and are not stored:

NameMeaning
_gotchaSpam trap (above)
_nextOverride the redirect for this one form (must be an allowed site)

Avoid _ for your own fields, or they will vanish.

Because a plain form POST is a page navigation, the browser ends up on the endpoint URL — your page can’t intercept it. So the endpoint sends the visitor back to your site:

  • Normally, to the Redirect after submit URL you configured.
  • To _next instead, if you sent one and it points at an allowed site.

If neither is usable, Minion shows a plain “we got your message” page. It works, but it is your visitor sitting on our domain looking at an unstyled page — set a redirect.

Minion has no thank-you page to customize. That page belongs on your site, where your design and your conversion tracking are.

List the sites the form lives on, one per line (https://example.com). Once the list is non-empty, submissions from anywhere else are refused.

  • Subdomains are not implied. https://example.com and https://www.example.com are two entries.
  • http:// and https:// are different too.
  • Leave the list empty and any site can submit. That is fine while you’re testing; fill it in before you ship.

If you’d rather handle the result yourself, post JSON. You get JSON back and no redirect.

const res = await fetch(endpointUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email, message }),
})
if (res.ok) { /* 201 { ok: true } */ }

Your site’s origin must be in Allowed sites for the browser to accept the response.

Fields per submission50
Field name length64 characters
Value length10,000 characters
Whole submission64 KB
Per IP10 per minute
Per endpointYour Daily limit (500 by default)

The daily limit is an abuse cap, not a quota — set it well above what you expect. Submissions past it are refused and not stored.

JSON requests get { "error": "<code>" }. Plain form posts get a minimal page with the same code on it.

CodeStatusWhat happened
not_found404Wrong token, or the endpoint was revoked
origin_not_allowed403The submitting site isn’t in Allowed sites
not_accepting403The form is unpublished, closed, or past its closing date
rate_limited429Too many submissions from one IP
daily_limit_reached429The endpoint’s daily limit
empty_submission400Nothing to store (every field was empty or reserved)
too_many_fields / value_too_long / payload_too_large400Over a limit above
attachments_not_supported400A file was attached

Revoke the endpoint. Forms already pointing at it stop working immediately, and submissions you’ve already received stay where they are. Revoking is not reversible from the embed’s point of view — a new endpoint gets a new URL, so you’d have to update the page.

One form can have several endpoints. Give each site its own, so you can turn one off without touching the others.