Headless submit
Use a Headless endpoint to send answers to a Makeform form from your own page or server. Makeform validates and stores the submission, then runs the form’s notifications and integrations.
Beta
This feature is available to all accounts. Its request and response format can change without a new API version. Do not depend on it for anything you cannot update quickly.
Request
POST https://submit.makeform.ai/f/ENDPOINT_IDUse the endpoint ID, not the form ID. The endpoint ID and submit URL are available from the Headless endpoint tools. Using a form ID returns 404 with {"ok":false,"error":"Endpoint not found","code":"endpoint"}.
Send a JSON object or an application/x-www-form-urlencoded body. JSON can be either a flat object of answers or an envelope with answers under responses.
The request body limit is 256 KB. multipart/form-data and other content types return 415. File uploads are not supported.
Authentication
For a browser request, the request’s Origin must match an allowed origin for the endpoint. CORS allows POST and OPTIONS, and the request headers Content-Type, X-Makeform-Session, and Idempotency-Key. Browser requests cannot send backend keys cross-origin.
For a server request with no Origin header, send the endpoint’s Headless key as a bearer token or in X-Makeform-Headless-Key:
Authorization: Bearer mfh_live_YOUR_KEYAnswer fields
Use each form field’s name as the answer key. In strict mode, field types and options are enforced. In loose mode, unknown keys are accepted and learned into the form’s schema.
| Field type | Accepted value |
|---|---|
| A valid email address | |
| Number, range slider, currency, opinion scale, rating | A value converted to a number |
| Single select, multiple choice | One of the field’s options as a string |
| Multi select, checkboxes | An array of values or a comma-separated string |
| Ranking, matrix | Values checked against the field’s rules |
| Address, geo, signature | An object or text |
| Other fields | Scalar text |
Hidden fields declared in form settings are accepted. Required fields depend on the respondent’s path through the form and which fields are visible. Calculations are recomputed on the server before validation.
A repeated key in a URL-encoded body becomes an array. For matrix fields, rows may also be sent as name.rowId keys.
Reserved keys
These keys are not stored as answers: _gotcha, _redirect, _session_id, _attribution, _idempotency_key, _email_verification, cf-turnstile-response, and _turnstile.
_redirect must be an HTTP or HTTPS URL whose origin is allowed for the endpoint. _attribution can contain page, referrer, and UTM values. For URL-encoded requests, send attribution as a JSON string or with bracket keys such as _attribution[page].
Responses
A successful submission returns 200:
{
"ok": true,
"message": "Submission successful!",
"submissionId": "SUBMISSION_ID"
}An idempotent replay returns 200 with the original submission ID and no message:
{
"ok": true,
"submissionId": "SUBMISSION_ID"
}If you send a valid _redirect, success returns 303 and redirects to that URL.
Validation errors return 400:
{
"ok": false,
"error": "Validation failed",
"code": "validation",
"errors": [
{
"field": "unknown_key",
"message": "Unknown field for this form"
}
],
"fieldErrors": {
"unknown_key": "Unknown field for this form"
}
}An idempotency conflict returns 409:
{
"ok": false,
"error": "This submission key was already used. Use a new key for different answers.",
"code": "idempotency_conflict"
}| Status | When |
|---|---|
400 | Validation, missing or failed Turnstile, disallowed redirect, unpublished form, or invalid idempotency key |
401 | Missing or invalid backend key; error is Missing or invalid API key, code auth |
403 | Origin not allowed; a Page form has its CAPTCHA setting enabled; or another endpoint restriction |
404 | Unknown, disabled, or unavailable endpoint |
409 | Idempotency conflict or schema changed during a loose-mode request |
413 | Body exceeds 256 KB; error is Payload is too large, code validation |
415 | Unsupported content type |
429 | Rate limited or monthly plan limit reached |
500 | Unexpected server error |
503 | Idempotency unavailable or schema learning unavailable |
Requests can be rate limited. When you receive 429, back off by the retryAfter value.
Partial saves
You can save a partial submission with POST https://submit.makeform.ai/f/ENDPOINT_ID/partial. Partial saves require a JSON body, an allowed browser origin, a Page form, strict mode, and the partial_submissions form setting. Include _session_id with 16–128 letters, digits, underscores, or hyphens. The endpoint returns {"ok":true}. A later final submission with the same session ID replaces the partial. Partial saves on a Headless-only form return 403.
Example
This example sends answers as JSON. Use an allowed origin for browser requests. For retries, you can send an idempotency key in the Idempotency-Key header or as _idempotency_key in the body.
curl -X POST https://submit.makeform.ai/f/ENDPOINT_ID \
-H "Authorization: Bearer mfh_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1234-attempt" \
-d '{"name":"Ada Lovelace","email":"[email protected]","message":"Please send me the product update."}'