Submit endpoint
Everything the public endpoint reads and returns. Field names are compatible with Web3Forms, so a form moved over keeps working.
Endpoint
Section titled “Endpoint”| 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. |
Request body
Section titled “Request body”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.
POST /submit HTTP/1.1Host: formwire.ioContent-Type: application/jsonAccept: application/json
{ "access_key": "YOUR-ACCESS-KEY", "email": "ada@example.com", "message": "Hello", "topics": ["pricing", "support"]}Option fields
Section titled “Option fields”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. |
Recognised fields
Section titled “Recognised fields”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. |
Responses
Section titled “Responses”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.
HTTP/1.1 200 OKContent-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"] }}HTTP/1.1 400 Bad RequestContent-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 }}Redirect
Section titled “Redirect”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 OtherLocation: https://example.com/thanksErrors
Section titled “Errors”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. |
Retries and idempotency
Section titled “Retries and idempotency”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.comRate limits and size caps
Section titled “Rate limits and size caps”- 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.
What’s stored with a submission
Section titled “What’s stored with a submission”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.