Skip to content

Submit endpoint

Everything the public endpoint reads and returns. Field names are compatible with Web3Forms, so a form moved over keeps working.

Property Value
URL https://formwire.io/submit
Key in the path https://formwire.io/submit/YOUR-ACCESS-KEY, for clients that can’t add a hidden field
Method POST. OPTIONS is answered for CORS preflight.
Authentication None. The access key identifies the form and can only create submissions for it.

Send the body as any of these; the field names are the same in each.

Content type Notes
multipart/form-data What a browser sends for a form with files. The only way to upload files.
application/x-www-form-urlencoded A browser’s default for a plain form.
application/json Must be an object. Arrays are kept as lists, nested objects are stored as JSON text, null values are dropped.

A field name ending in [], such as a checkbox group named topics[], is stored as a list under topics. A name repeated without the brackets is stored as a list too.

A JSON request
POST /submit HTTP/1.1
Host: formwire.io
Content-Type: application/json
Accept: application/json
{
"access_key": "YOUR-ACCESS-KEY",
"email": "ada@example.com",
"message": "Hello",
"topics": ["pricing", "support"]
}

These fields change what Formwire does with a submission and are not stored with it. Everything else you send is kept as the submission’s data, shown in the email and the dashboard under its field name.

Field What it does Notes
access_key Your form’s access key. Required unless it’s in the URL path. A UUID from the form’s Settings, General. Example: a3f1c2…-….
subject The notification email’s subject. Up to 200 characters. {field:name} placeholders are filled in. Example: New enquiry from {field:name}.
from_name The sender name shown on the notification email. Up to 80 characters. Example: Website contact form.
replyto Where replies go instead of the sender’s email. An email address, up to 254 characters. Example: sales@example.com.
redirect The page people see after a plain HTML form submits. http or https on one of the form’s allowed domains, up to 2048 characters. Example: https://example.com/thanks.
fax The honeypot. Leave it empty; anything in it marks the submission as spam. The default name. Rename it in the form’s spam settings.
botcheck The Web3Forms honeypot, still honoured. Spam unless empty, “false” or “0”.
h-captcha-response The hCaptcha token when the form has hCaptcha switched on. Set by the captcha widget. Example: P1_eyJ….
g-recaptcha-response, cf-turnstile-response The reCAPTCHA or Turnstile token when the form uses one. Set by the captcha widget, verified and dropped.
ccemail, webhook, recaptcha_response Web3Forms options. Set CC emails, webhooks and reCAPTCHA in the form’s settings instead. Dropped, so a moved form doesn’t show them as data.

These are stored like any other field, and also read for the sender’s name, email and phone. Names are matched without case, spaces, dashes or underscores, so First Name, firstName and first_name all work.

Field Also accepted What it does
name full_name, your_name, contact_name The sender’s name, shown in the From column.
first_name + last_name fname, given_name, forename; lname, surname, family_name Joined into the sender’s name when there’s no name field.
email email_address, your_email, mail, contact_email The sender’s email. Replies and the confirmation email go to it. Up to 254 characters.
phone phone_number, tel, telephone, mobile, mobile_number, cell, cell_phone, contact_number The sender’s phone number.
topics[] any name ending in [] A checkbox group, stored as one list under topics.
type="file" Uploaded files (multipart only), linked from the notification email and listed under the field’s name.

The endpoint answers in JSON when the request says it wants JSON: an Accept header containing application/json, or a JSON body. Otherwise it answers a plain form post with a redirect.

A stored submission returns 200 with the new submission’s id and the fields as stored. A spam submission caught by the honeypot gets the same response, so a bot learns nothing.

Success
HTTP/1.1 200 OK
Content-Type: application/json
{
"success": true,
"message": "Submission received.",
"data": {
"submission_id": "7c1e9b2a-4f3d-4e8a-9b1c-2d5f6a7b8c9d",
"email": "ada@example.com",
"message": "Hello",
"topics": ["pricing", "support"]
}
}
Error
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"success": false,
"message": "The access key is missing or invalid.",
"error": {
"code": "invalid_access_key",
"message": "The access key is missing or invalid.",
"details": null
}
}

A plain form post gets a 303 to the redirect field if it’s present, otherwise to the redirect URL in the form’s settings, otherwise to a hosted thank-you page at /submit/success. Only http and https URLs are followed. On an error the browser is sent to /submit/error?code=…, a hosted page that explains the code; nothing else from the request appears on it.

HTTP/1.1 303 See Other
Location: https://example.com/thanks

Every error carries success: false, a readable message and an error.code to branch on.

Status Code When
400 invalid_request The body couldn’t be read, or a JSON body isn’t an object.
400 invalid_access_key The access key is missing or isn’t a valid key.
403 form_paused The form is paused in the dashboard.
403 form_suspended The form was suspended by Formwire.
403 domain_not_allowed The page posting the form isn’t in the form’s allowed domains.
403 file_uploads_unavailable The form doesn’t accept files.
403 storage_limit_reached The organization’s file storage is full.
404 not_found The path isn’t /submit.
413 payload_too_large The body is over the size cap, or the stored fields are over 64 KB.
415 file_type_not_allowed A file’s type isn’t accepted.
429 rate_limited Too many posts from this IP or to this key, or the form’s abuse ceiling was reached.
500 internal_error Something failed on Formwire’s side. Retry with the same Idempotency-Key.

Send an Idempotency-Key header to make a retry safe. A second post to the same form with the same key returns the submission stored the first time instead of creating another.

curl https://formwire.io/submit \
-H "Accept: application/json" \
-H "Idempotency-Key: order-8831-contact" \
-F access_key=YOUR-ACCESS-KEY \
-F email=ada@example.com
  • 30 posts a minute from one IP address, and 120 a minute to one access key. Over either, the answer is 429 rate_limited.
  • Your plan’s submission allowance is a soft quota: you’re told when you pass it and delivery continues. Only unusual volume far past it is stopped.
  • A body of up to 128 KB, plus files on a multipart post: up to 10 files, 10 MB each and 25 MB together, on forms that accept uploads.
  • Up to 200 fields with names of up to 100 characters; the stored fields together fit in 64 KB.
  • The page URL (from Referer) is kept to 2048 characters and the user agent to 512.

The endpoint allows any origin, so you can call it with fetch from a browser on any site. Preflight permits POST with the Content-Type, Accept and Idempotency-Key headers and is cached for a day. To limit which sites can post to a form, use allowed domains: the check reads the request’s Origin (or Referer) and refuses the rest with 403 domain_not_allowed.

Besides the fields: the page URL the post came from, the sender’s IP address and user agent, the time, and the delivery options (from_name, replyto). The honeypot field and the option fields above are never stored.