Headless Forms
Create a backend-only Makeform form, then POST submissions to its HTTPS endpoint from your own website, app, or server. Responses land in your Makeform inbox and can run the form's enabled notifications and integrations.
Private preview — Headless creation is currently available only to selected creators. Free includes 1 enabled endpoint and 50 Headless submissions per month. Pro and Business include unlimited enabled endpoints and Headless submissions. Respondent auto-reply is paid-only.
Headless-native forms do not have a hosted /f/... page. The endpoint is their
public collection surface.
Quick start
- From Create form, choose Headless endpoint. Name it and add the website domain that will submit.
- Makeform creates a Headless-native form and endpoint. You'll get a URL like:
https://www.makeform.ai/api/headless/f/hf_xxxxxxxxxxxxxxxx- Confirm every browser origin you use (for example,
https://example.comandhttps://www.example.com) or generate an API key for server-side use. - Keep the endpoint enabled and point your client at it:
Plain HTML
<form action="https://www.makeform.ai/api/headless/f/hf_XXXX" method="POST">
<input type="text" name="name" required />
<input type="email" name="email" required />
<input type="text" name="_gotcha" style="display:none" tabindex="-1" />
<input type="hidden" name="_redirect" value="https://example.com/thanks" />
<button type="submit">Send</button>
</form>JavaScript (fetch)
await fetch('https://www.makeform.ai/api/headless/f/hf_XXXX', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Ada', email: 'ada@example.com' }),
})Server-side (cURL + API key)
curl -X POST https://www.makeform.ai/api/headless/f/hf_XXXX \
-H "Authorization: Bearer mfh_live_..." \
-H "Content-Type: application/json" \
-d '{"name":"Ada","email":"ada@example.com"}'Authentication
Every request must satisfy one of two modes:
| Mode | How | Use for |
|---|---|---|
| Browser | The request's exact Origin (scheme + host + optional port) matches an Allowed Origin (CORS enforced) | Forms on your own website |
| API key | Authorization: Bearer <key> or X-Makeform-Headless-Key: <key> header | Servers, scripts, mobile apps |
API keys are shown once at creation and can be rotated or revoked at any time from the Headless settings. Rotating immediately invalidates the previous key.
Request format
Send application/json or application/x-www-form-urlencoded (what a plain HTML form sends). multipart/form-data is not supported — don't set enctype on your form. Field names must match your form's field names.
Reserved fields (never stored as answers):
| Field | Purpose |
|---|---|
_gotcha | Honeypot. Render it hidden and empty — any value silently drops the submission as spam |
_redirect | URL to redirect the browser to after success (303). Must be on one of your Allowed Origins |
_session_id | Optional session id for linking partial submissions |
cf-turnstile-response (or _turnstile) | Cloudflare Turnstile token, when Turnstile is enabled |
Validation
Submissions are validated against your published form:
- Strict mode (default): unknown fields are rejected; required fields and types are enforced. Best when you control the client.
- Loose mode: unknown fields are accepted, stored, and learned into the Headless-native form schema. Primitive arrays are learned as multi-value fields; complex values are safely normalized before storage. Useful when the payload evolves, but review learned fields before switching to strict mode.
Validation failures return 400 with a machine-readable errors object naming each offending field.
Spam protection
- Honeypot (default): include the hidden
_gotchafield; bots that fill it are silently dropped (the request still returns success, so bots learn nothing). - Cloudflare Turnstile (optional): add your Turnstile site key + secret in Headless settings, then include the widget's
cf-turnstile-responsetoken in browser submissions. API-key (server) requests skip Turnstile. - Rate limits apply to every endpoint.
Responses
| Status | Meaning |
|---|---|
200 | { "ok": true } — submission stored |
303 | Success with _redirect — browser is redirected |
400 | Validation failed — body includes errors, or the form is not published |
401 / 403 | Missing/invalid API key, or origin not allowed |
404 | Endpoint doesn't exist or is disabled |
429 | Rate limited — or the Free plan's 50 submissions/month reached (the error message says which) |
Limits
| Free | Pro / Business | |
|---|---|---|
| Enabled endpoints | 1 | Unlimited |
| Submissions / month | 50 | Unlimited |
| Rate limits | Standard | Standard |
The monthly quota counts completed Headless submissions across personal forms
and forms owned by your teams during the UTC calendar month. When a Free
account hits it, requests return 429 until the month rolls over or the billing
owner upgrades.
FAQ
Do integrations fire for Headless submissions? Enabled integrations and owner notifications run through the normal submission service. Owner notification is enabled by default at creation and can be opted out. Respondent auto-reply is available on Pro and Business. Headless entries are tagged with their source in your inbox.
Can I use one endpoint for several websites? Yes — add each site's origin to Allowed Origins.
File uploads? Not yet supported over the headless endpoint — use a hosted or embedded form for file fields.
CORS errors in the browser? The exact origin (scheme + host + port) must be listed in Allowed Origins. https://www.example.com and https://example.com are different origins.
The endpoint returns 404 even though the URL looks correct? The endpoint
may be disabled, deleted, owned by an account outside the current preview, or
attached to a suspended/deleted form. A disabled endpoint deliberately looks
missing.
The endpoint returns 401 or 403 from a server? Do not send a browser
Origin header from server code. Send a current mfh_live_... key in either
supported authentication header, and rotate the key if it may have leaked.
Can I submit multipart data? No. Send JSON or URL-encoded fields. File
uploads, multipart/form-data, and file-field payloads are not supported.