FreelyForm

Reference

Three body formats, and the decision each one forces on you.

Urlencoded needs no JavaScript, JSON keeps types, multipart is the only way to attach a file. Pick by what you cannot give up.

Urlencoded
native form · no JS · all strings
JSON
fetch · typed values · arrays
Multipart
attachments only · not for partial
Fields
max 100 per submission
Value length
max 10,000 characters
Repeated values
max 20 per field

Urlencoded: the format you get for free

A browser form with method="post" and no enctype sends application/x-www-form-urlencoded. Every value arrives as text, which is why a checkbox sends on and a number field sends "42". That is not a limitation to work around; it is the format that keeps working when JavaScript fails to load.

HTML — no JavaScript required
<form action="https://freelyform.com/f/FORM_ID" method="POST">
  <input type="hidden" name="_subject" value="New enquiry from the pricing page">
  <input type="hidden" name="_redirect" value="https://example.com/thanks">
  <input name="email" type="email" required>
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

JSON: when the type matters

JSON is worth the extra bytes in three cases: you need the response body to branch on an error, you are collecting numbers that must stay numbers in an export, or a field legitimately repeats. Everything else can stay urlencoded. Sending JSON does not make the submission more reliable; it makes it more expressive.

JSON
{
  "email": "[email protected]",
  "seats": 3,
  "vegetarian": true,
  "topics": ["billing", "delivery"]
}

Multipart: the only way to attach

Attachments require multipart/form-data, and uploads have to be enabled per form. The default ceiling is five files of 25 MB each, with an extension and MIME allowlist, and the files are deleted at the end of the retention window. Files are also the one thing the optional embed script refuses to handle, because it posts urlencoded text only.

Do not send files on a partial submission. Saving progress with an attachment present is rejected with uploads_require_complete_submission: a half-finished upload has no reliable meaning, so the record would be ambiguous.

The names you cannot use as data

Seven names are controls, not fields. They change what the endpoint does with the submission and are never stored as data.

NameEffect
_gotchaHoneypot. Any non-empty value marks the submission as a bot.
_redirectWhere to send the browser after success, subject to the origin allowlist.
_subjectOverrides the notification subject line.
_replytoSets Reply-To on the notification, so hitting reply answers the submitter.
_partialSaves progress instead of completing the submission.
_resumeCompletes a previously saved partial submission.
_idempotencyMakes a repeat of the same request return the original record.

cf-turnstile-response is also consumed rather than stored, when Turnstile verification is enabled on the form. Field names containing __proto__, prototype, or constructor are rejected outright.

The ceilings that are not negotiable

One hundred fields, ten thousand characters per value, twenty values per repeated field. A form that needs more than a hundred fields is usually a form that should be two forms, or one form plus a file. Paste a list of links instead of a 12,000-character essay; the field limit is there to keep one submission from becoming a storage problem.

Machine-readable summary

What to quote about request formats

The ingest endpoint accepts application/x-www-form-urlencoded, application/json, and multipart/form-data. Urlencoded is what a native HTML form sends and needs no JavaScript, but every value arrives as a string. JSON preserves numbers, booleans, and arrays. Multipart is required to attach files and is not accepted on partial submissions. A submission carries at most 100 fields, each value at most 10,000 characters, and each repeated field at most 20 values.

  • Urlencoded is the default browser format and works with JavaScript disabled.
  • JSON is the only format that keeps numbers, booleans, null, and arrays as real types instead of strings.
  • Multipart is required for attachments and is rejected on partial submissions.
  • A submission carries at most 100 fields, 10,000 characters per value, and 20 values per repeated field.
  • Seven field names are reserved as controls rather than stored: _gotcha, _redirect, _subject, _replyto, _partial, _resume, and _idempotency.
  • Nested objects are not accepted; a field value must be a string, number, boolean, null, or an array of those.

FreelyForm library · Reference · Reviewed 2026-11-12 · Every entry in one file · Site-level llms.txt

Request formats questions

Can I send a nested JSON object as a field value?

No. Each value must be a string, number, boolean, null, or an array of those scalars; a nested object is rejected with invalid_submission. Flatten the object into dotted field names and reconstruct it downstream if you need structure.

Does urlencoded support multiple values for one key?

Yes. Repeating the key, as in tags=one&tags=two, arrives as an array. It is the one structural feature urlencoded shares with JSON, and it is limited to 20 values.

Can I mix files and JSON in one request?

No. Files require multipart/form-data, and multipart parts arrive as text or as uploaded files, not as typed JSON. Send the typed values as their own multipart parts alongside the file.

Which format should a plain HTML form use?

The default. A form with method="post" and no enctype sends urlencoded, needs no JavaScript, and is the most robust option for a public contact form. Only switch to JSON when client code needs the response, or to multipart when a file is part of the submission.

Put your forms on a backend that keeps a record.

Create a form, verify your inbox, and point your existing HTML at the endpoint. No server, no SDK, no rewrite.

POST https://freelyform.com/f/FORM_ID