Troubleshooting

Fix a form that does not deliver: wrong endpoint or method, missing access key or names, CORS, redirects, spam folder and every error code.

Most form problems come down to a handful of causes: the form is not posting where you think, a field is missing its name, or a setting in the dashboard is doing exactly what it was told to. Find your symptom below. If you see an error message, jump to Error messages by status code, which lists every message the endpoint returns and what to do about it.

Nothing arrives in my inbox#

The visitor saw a success message (or the thank-you page), but no email turned up. Work through these in order.

1. Check spam and other folders. Submission emails come from a SilentForms address, not your own domain, so a new inbox may file the first few as spam or under Promotions or Updates. Mark one as "not spam" and add the sender to your contacts. Setting a sender name makes them easier to find later.

2. Check which email the key belongs to. A key sends to the address that confirmed it, nothing else. If you created keys with more than one address, or copied the form from an old project, the submissions may be going to another inbox. Sign in to the dashboard with the address you expect: every key issued to it is listed there, with its submissions.

3. Check that email notifications are on. In the dashboard, a form with "Email me every submission" turned off only stores submissions (and sends them to the webhook, if one is set). They are still in the form's submissions view.

4. Check the honeypot. A submission with the botcheck field filled in gets a normal success response but is never emailed. That is by design, so bots learn nothing. Look in the form's spam view in the dashboard: if your test submissions are there, the honeypot is being filled by a real browser. Common causes:

  • the checkbox is visible (a CSS change, a page builder that strips style, or a theme that styles every checkbox),
  • it was turned into a text input that a password manager or autofill extension fills in,
  • JavaScript code sends botcheck: true or "on" for every request.

Keep it exactly as <input type="checkbox" name="botcheck" style="display: none;" tabindex="-1" autocomplete="off">. An unchecked box is not sent at all; a value of false or an empty string is also fine. See Honeypot.

5. Check that the last test really succeeded. When testing over and over you can hit the limit of 10 submissions a minute from one IP address. That returns a 429 error, so a plain HTML form lands on the error page rather than the thank-you page, which is easy to miss in a quick test. Wait a minute and try again.

I lost my access key#

Request a key again on the access key page with the same email address. The confirmation link shows the key you already have, so nothing changes on your forms and pages already using it keep working. If you have several forms, sign in instead: the dashboard lists every form with its key.

If you think the key has leaked rather than been lost, rotate it in the dashboard instead. See the dashboard.

The form redirects to an error page#

A plain HTML form (no JavaScript) never shows errors inline: on any error the browser is sent to the SilentForms error page with the reason in the message. Read the message there and find it in the table below. Errors always go to that page; the redirect field and the dashboard redirect only apply to successful submissions.

If you want to show errors on your own page, submit with fetch instead. See AJAX form with JavaScript.

The page shows a 405 or "Send a POST request"#

The form is sending a GET request. A <form> without method="POST" defaults to GET, which puts the fields in the URL and is refused with 405. Add method="POST", and check that action is exactly https://silentforms.com/api/submit (or one of the key-in-URL forms on the overview).

Error messages by status code#

Every error has the same text in the JSON message field and on the error page.

StatusMessageFix
400The form has no fields to submit.Every field is empty or has no name. Give each input a name attribute.
400Email address is invalid.The email field has a value that is not an email address. Use type="email" on the input so the browser checks it first.
400Reply-to address is invalid.replyto or _replyto is not a single valid address. See Reply-To address.
400redirect must be an absolute http(s) URL.Use a full URL such as https://example.com/thanks, not /thanks.
400webhookUrl must not point at a private or reserved address.The webhook must be a public https:// or http:// URL. Localhost and private networks are refused. See Webhooks.
400Request body is not valid JSON.Your code sent a broken JSON string. Build the body with JSON.stringify.
400JSON body must be an object.Send { ... }, not an array or a bare string.
400Captcha is required.CAPTCHA is on for this form but no token arrived. See CAPTCHA errors.
400Captcha verification failed. Please try again.The token was rejected by the provider. See CAPTCHA errors.
401Access key is required.No accessKey field (and no key in the URL). Check the hidden input's name and, in JavaScript, that the variable holding the key is not empty.
403Invalid or inactive access key.The key has a typo, was revoked after a rotation, belongs to a deleted form, or the form is paused in the dashboard.
403This form does not accept submissions from this domain.The form has an allowed-domains list that does not include the site you posted from. See Restrict to your domain.
403This form has been suspended for breaking the acceptable use policy.The form was suspended after misuse (see the acceptable use policy). The owner sees a notice in the dashboard and can write to the contact address if it is a mistake.
405Send a POST request with your form data to this endpoint. See the docs for details.The request was a GET. Add method="POST".
413Submission is too large.The request is over 1 MB. Usually a file input in a multipart/form-data form: remove it.
415Unsupported Content-Type. Send form data or JSON.Send application/x-www-form-urlencoded, multipart/form-data or application/json.
429Too many requests. Please try again in a minute.More than 10 submissions a minute from one IP. Wait for the time in the Retry-After header.
429This form is receiving too many submissions. Please try again shortly.More than 60 submissions a minute to one key.
500Could not deliver your message. Please try again later.The email (and webhook, if it was the only delivery) could not be sent. Try again; if it persists, check your webhook endpoint.
503Captcha could not be verified right now. Please try again in a moment.The CAPTCHA provider did not answer, or the saved secret key no longer works. The submission was refused; retry after 30 seconds. If it keeps happening, re-enter the secret key in the dashboard.

"Is this a CORS error?"#

Almost certainly not a SilentForms restriction. The endpoint accepts requests from any origin and answers the browser's preflight (OPTIONS) request, so fetch works from any domain, localhost and static hosts alike.

If the browser console does report a CORS failure, look for these instead:

  • An extra request header. Only Content-Type and Accept are allowed. Adding Authorization, X-Requested-With or a custom header makes the preflight fail. Remove it.
  • The wrong URL. A typo in the endpoint, or a redirect to an HTML page, can surface in the console as a CORS error. Copy the URL from the overview.
  • mode: 'no-cors'. This hides the response from your code, so you can never read the result. Remove it.

A field is missing from the email#

  • It has no name. Browsers only send inputs with a name attribute; id alone is not enough. This is the most common cause, especially with page builders and UI component libraries.
  • It is a special field. subject, replyto, from_name, redirect and webhookUrl steer delivery and are removed from the data. A visible input named subject becomes the email subject, not a row in the body. Rename it (for example topic) if you want it as data.
  • It is a file input. Uploads are not supported; file inputs are ignored.
  • It is disabled. Browsers do not submit disabled inputs. Use readonly if the visitor should not edit it.
  • It is an unticked checkbox. Unticked boxes are not sent at all.

Only one checkbox value arrives#

Checkboxes that share a name all arrive as long as the browser sends them. With JavaScript, Object.fromEntries(new FormData(form)) keeps only the last value of a repeated name. Collect repeated names into arrays instead; Checkboxes and multi-select has a helper.

CAPTCHA errors#

  • "Captcha is required." The widget is not on the page, is outside the <form> element, or the request was sent before the visitor finished it. With fetch, read the fields with new FormData(form) so the token field the widget adds is included.
  • "Captcha verification failed." The token expired (tokens are short-lived and single-use), was already used, or comes from a different provider than the one saved in the dashboard. Reset the widget after every submission. For reCAPTCHA v3, the score may be below the form's minimum.
  • 503. The provider did not answer, or the saved secret is no longer valid at the provider.

Turn CAPTCHA on in the dashboard only after the widget is live on your page, otherwise every submission is refused. The provider guides cover testing: Turnstile, hCaptcha, reCAPTCHA.

Test with curl#

curl takes your site out of the picture. If this works, the key is fine and the problem is in your form markup or code:

curl -i -X POST https://silentforms.com/api/submit \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"accessKey":"YOUR_ACCESS_KEY_HERE","name":"Test","email":"[email protected]","message":"Hello from curl"}'

-i prints the status line and headers, so you can see the code and any Retry-After. A working key answers:

{ "status": "success", "success": true, "ok": true, "message": "Form submitted successfully." }

To test exactly what a plain HTML form sends, post form-encoded data instead. Without Accept: application/json you get a 303 redirect, and the Location header shows where the visitor would land:

curl -i -X POST https://silentforms.com/api/submit \
  -d accessKey=YOUR_ACCESS_KEY_HERE \
  -d name=Test \
  -d message=Hello

If the form uses domain restriction with "Also block requests that send no Origin", curl is refused with 403. Add -H "Origin: https://your-site.example" with your real domain to test.

Next steps#