Website Forms: Save the Lead Before Showing Sent
A sent message is a choice the page makes after it handles a submission. On website forms, that choice can come before the note reaches the dashboard or inbox where the business actually works. The June 2022 HTTP Semantics standard says that a 200 response means the request has succeeded, and that the content of a 200 response to POST is the status of the action or the results obtained from it. [1] We do not show a sent confirmation until the lead and a delivery record for that same lead are saved, and we retry the delivery when the handoff does not finish.
What a sent screen actually means
The visitor wants a person at the business to see the note. The browser only knows whether the server it called accepted the request and sent a successful response.
In that June 2022 standard, the 2xx class means the request was successfully received, understood, and accepted. [1] The same standard lists one use of POST as providing the fields entered into an HTML form to a data-handling process. [2] A thank-you page can be the body of the success response. It still does not describe a second system that was never called.
The standard also has a code for work that is not finished. A 202 response means the request has been accepted for processing, but the processing has not been completed, and the response is intentionally noncommittal. [3] Plenty of forms never send 202. They send 200 and print thanks as soon as their own handler returns. If the owner watches a dashboard or another inbox, that thanks can run ahead of the handoff.
One forward, and a count that did not match
On one site we run, we saw 10 submissions stored with the form. We saw 9 of them on our dashboard. Each submission was forwarded once, with no retry.
That is the mismatch we saw. The site held the rows. The dashboard count was short of the site count. We are not describing what happened to any person after that. The design point is the single forward. Nothing tried the handoff again.
The June 2022 HTTP/2 standard states the general trap. An HTTP client is, in general, unable to retry a non-idempotent request when an error occurs, because there is no means to determine the nature of the error. Some server processing may already have happened, and repeating the request can repeat that effect. [4]
POST is in the group you do not blindly repeat. The June 2022 HTTP Semantics standard says a method is idempotent when the intended effect of repeating it matches the effect of doing it once. Of the methods it defines, PUT, DELETE, and the safe methods are idempotent. [5] A client should not automatically retry a non-idempotent method unless it knows the request is actually safe to repeat, or knows the original request was never applied. [5]
The June 2022 HTTP/1.1 standard names a plain failure. A connection can close before the client has the responses it is waiting on. Idempotent methods can be retried after a connection failure. A user agent should not pipeline further requests after a non-idempotent method until the final status code for that method has been received, unless it can detect and recover from a partial failure. [6]
Sending the visitor's POST a second time can create a second submission if the first request actually landed. Stopping after one forward is how the site count and the dashboard count drift apart. We save one lead, then retry delivery of that saved lead. We do not ask the visitor to click again so a second POST can cover for the first.
Save both records before the visitor sees sent
The browser sends the form. We check the submission on the server. We write the lead and a delivery record for that same lead as one save. The sent confirmation is what we return only after that save succeeds. If the save does not succeed, the visitor does not see sent. The response says the submission was not saved.
The delivery record is the handoff still owed to the dashboard or inbox the business watches. If that job does not finish, we retry the job from the record we already stored. The retry does not mint a new lead because the first attempt was quiet.
We do not claim that every retry finishes, or that a submission can never fail to arrive. Other apps go down. A job can sit until someone looks. What we will not do is call the form sent before those two records exist.
A browser check cannot make that save decision by itself. A February 2023 web.dev guide on HTML forms says validation must be both client-side and server-side. [7] The browser check helps someone fix a typo early. The server check decides whether this submission becomes a lead and a delivery record, or an error instead of a thank-you.
Keep the form working when script does not run
Script can clarify the page. The lead still has to be stored if that script never runs.
The HTML Living Standard's introduction to forms, which that standard marks as non-normative, says no client-side scripting is needed in many cases, and that writing a form includes the server-side processing. [8] The normative steps are stricter. The browser fires a cancelable submit event. If that event does not continue, the submission algorithm returns and does not proceed. [9] Cancel the event in script, then fail inside that script, and the click can store nothing, because the built-in path never ran.
If validation is on and the result is negative, the algorithm stops before the request is sent. [9] If the event continues and the form is an HTTP or HTTPS POST, the browser submits the entry list as the request body and navigates to the response. [9] The same February 2023 guide says a form can be submitted, processed, and answered with a new page without any JavaScript, using the form's action and method. [7] We keep that path. If script changed the words to sent before the save came back, the visitor could leave with no row stored.
An optional website field should not block the lead
A strict field can stop a real submission before delivery work starts. The HTML Living Standard says that while a URL field's value is neither the empty string nor a valid absolute URL, the field is suffering from a type mismatch. [10] Empty is allowed, which is what optional should mean. A non-empty value that fails the absolute-URL check is not. If validation is running, a negative result stops the submission before the browser sends the form. [9] One extra box can then veto the name, the phone number, and the message beside it.
In our tests, an optional website field refused 12 of 15 messy entries. It now refuses none of those entries.
Messy means the note a person types when the box is optional: a bare name, a half-finished address, or words that are not a full URL. The old check refused the whole submission over that note. The field we ship now keeps the note and lets the lead through.
When a format really is required, say so in the label. The December 2024 WCAG 2.2 recommendation says labels or instructions are provided when content requires user input. [11] W3C's understanding notes for that criterion say instructions may specify data formats when those formats are outside the customary ones, and the page calls the notes informative explanations rather than what is required to meet WCAG. [12] Nothing here states that a form conforms to WCAG. If you will reject a value, show what the field expected. If you will not, do not let a strict URL check block the lead.
Say it in text when the save does not happen
The failure page should make the miss obvious. The December 2024 recommendation says that if an input error is automatically detected, the item that is in error is identified and the error is described to the user in text. [11] W3C's understanding notes for that criterion say that, on an unsuccessful form submission, showing the form again with no hint is not sufficient, and the error must be indicated in text. [13] That page labels the notes as informative explanations, not as the test for meeting WCAG. [13]
If we cannot save, we say the submission was not saved and we point at what blocked it. We do not return a blank form in silence, and we do not print sent. Sent follows a successful save of the lead and the delivery record.
A quiet thank-you is how a count mismatch stays invisible. The visitor leaves. The owner sees an ordinary dashboard. A clear error covers a refused save. A delivery record that retries covers a save whose handoff is not done yet.
When a builder or a CMS form is enough
WordPress, Shopify, or a site builder can be the better fit when its form already stores each submission and can retry a failed notification from that stored row, and when the owner will actually edit there. A plain form someone maintains beats a custom one nobody opens.
For most businesses, a site built for the way they take work still beats a one-size-fits-all builder. If the builder's form only fires one message and keeps no copy you can reopen, it has the same single-forward hole. The visitor can see thanks while that handoff was a single request. In that case we build the save-before-sent order, and we keep the form posting with script turned off.
Our studio is in Charlotte, and this is the check we want on the forms we ship. We will say when a plugin or a hosted form already meets the bar, and when it does not. The question is where the lead is stored and how delivery is retried.
If you compare options, submit a test and find the row both in the site's storage and in the tool the team watches. Turn script off and submit again. Put a rough note in any optional website box, and see whether a missing second copy is tried again without a duplicate lead.
Practical takeaways
- Show sent only after the lead and a delivery record for that lead are saved. If the save fails, say so in text and do not show sent.
- Retry delivery of the saved lead. Do not retry by repeating the visitor's POST, and do not stop after one forward.
- Keep the plain POST working with no script. A February 2023 web.dev guide, and the HTML submission steps, both describe a form that can be sent and answered as a normal page load. [7] [9]
- Check values on the server as well as in the browser. That February 2023 guide says validation belongs on both sides. [7]
- Do not let an optional website field block the rest of the form. In our tests, that kind of field refused 12 of 15 messy entries.
- The field we ship now refuses none of those entries.
- On one site we run, we saw 10 submissions stored with the form.
- We saw 9 of them on our dashboard. Each of those submissions was forwarded once, with no retry.
- When a format is truly required, label it and describe any error in text. The December 2024 WCAG 2.2 recommendation states those duties. W3C's understanding notes are guidance, not a conformance claim. [11] [12] [13]
How we can help
Have more questions or want to get in touch? Reach Idea Forge Studios through our contact page, call (980) 322-4500, or email [email protected].
Citations
- RFC Editor, "RFC 9110: HTTP Semantics" (2022-06)
- RFC Editor, "RFC 9110: HTTP Semantics" (2022-06)
- RFC Editor, "RFC 9110: HTTP Semantics" (2022-06)
- RFC Editor, "RFC 9113: HTTP/2" (2022-06)
- RFC Editor, "RFC 9110: HTTP Semantics" (2022-06)
- RFC Editor, "RFC 9112: HTTP/1.1" (2022-06)
- web.dev, "Forms" (2023-02-21)
- WHATWG, "HTML Standard" (2026-10-20)
- WHATWG, "HTML Standard" (2026-10-20)
- WHATWG), "HTML Standard" (2026-10-20)
- W3C, "Web Content Accessibility Guidelines (WCAG) 2.2" (2024-12-12)
- W3C, "Understanding Success Criterion 3.3.2: Labels or Instructions" (2026)
- W3C, "Understanding Success Criterion 3.3.1: Error Identification" (2026)