Skip to content

Submit API

One endpoint receives every submission:

POST https://api.formwire.io/submit

The endpoint accepts three body encodings; pick whichever your stack produces:

Content-TypeTypical source
application/x-www-form-urlencodedplain HTML <form> posts
multipart/form-dataforms with enctype set (required for file uploads)
application/jsonfetch / axios / server-side calls

Every request must include your access_key (as a hidden field or JSON property). All other fields are yours: any named value becomes part of the stored submission and the notification email. Fields starting with an underscore are reserved.

CORS is handled: preflights succeed and JSON responses are readable from browser fetch.

{
"success": true,
"message": "Submission received",
"id": "sub_...",
"update_token": "..."
}
  • id identifies the stored submission.
  • update_token lets the same client amend that submission (for example, multi-step forms updating one record).

Two variations:

  • Redirect: if the request is a regular form post and includes an allowlisted _redirect URL, the response is a 302 to that URL instead of JSON.
  • Spam: submissions caught by spam checks receive a generic {"success": true, "message": "Submission received"} with no id or update_token. Bots are not told they were filtered.

A successful create returns an update_token. To amend that submission later — multi-step forms, “save progress”, abandoned-cart capture — POST to the same endpoint with the id and token instead of creating a new record:

{
"access_key": "YOUR-KEY",
"_submission_id": "sub_...",
"_update_token": "...",
"phone": "+1 555 0100"
}
  • Fields are merged into the stored data — new keys are added, existing keys overwritten — so you only send what changed.
  • Files can be attached on an update too (same multipart/form-data rules as file uploads).
  • Captcha is a one-time gate at first submit, so updates skip it; rate limits still apply.
  • The response returns the same id and update_token, so you can keep updating.

Add _notify=1 to the final update to deliver the completed submission — it sends the notification email, auto-reply and webhooks once (and meters it once). A typical flow: create the record silently on step 1, update as the user progresses, then finalize with _notify=1. Delivery only fires if the original submission is itself deliverable (not spam or a preview).

Update errors: 403 Invalid submission id or update token, 404 Submission not found.

All errors share one shape: {"success": false, "message": "..."}.

StatusMessageMeaning
400Invalid request bodyBody could not be parsed in any accepted format
400access_key is requiredNo key in the request
403Invalid access_keyKey is malformed
403Invalid or disabled access_keyKey does not exist or the form is disabled
403Submissions are not allowed from this domainThe form has domain rules and the request origin is not on the allowlist
403Captcha verification failedA captcha provider is enabled and the token failed server-side verification
403Invalid submission id or update tokenAn update referenced a submission that is not yours
404Submission not foundUpdate targeted a submission that no longer exists
413Payload too largeBody exceeds the size limit
429Too many submissions, please slow downRate limit hit; retry after a pause
503Temporarily unavailable, please retry / Could not record submission, please retryTransient storage issue; safe to retry

Handle 429 and 503 with a retry; treat 4xx as a bug in the integration.

Every accepted submission is stored durably before the API acknowledges it. Email delivery then runs asynchronously with retries; you can see per-submission delivery status in the dashboard. A crash or mail-provider hiccup cannot lose an acknowledged submission.