Your first HTML form
Create an HTML contact form with Qubeforms using a standard form action. Submit without JavaScript and configure redirects, email delivery, and spam protection.
1. Add the external form stylesheet
Save the CSS below as a separate file in your website. HTML examples load it from /static/assets/css/form-helper.css; React and Vue import ./form-helper.css beside the component. Adjust the path to match your site and confirm the file loads before publishing. The generic .form-helper class moves the text input off-screen without the hidden attribute or display: none. Keep tabindex="-1", autocomplete="off", and aria-hidden="true". New forms use fax_number; existing forms keep their saved name. Match Settings → Delivery & spam → Honeypot field in HTML, JSON, and drafts. To rename an existing trap, update both the setting and the website together. Never use a name that also collects a real visitor answer.
.form-helper {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
pointer-events: none;
}2. Create your form endpoint
Sign in, open Forms, enter a form name and the origin of your website, and select Create endpoint. Open Manage → General in the form settings and copy the submission endpoint. Keep the form Active. Use the public cf_pk_ key in your website; private read keys and your account password are never needed in an HTML form.
3. Add a labeled HTML form
Replace cf_pk_YOUR_KEY with your public key. Each submitted control needs a name, and each visible control needs a label. This example works without JavaScript when the form does not require CAPTCHA. Keep the honeypot empty and use your configured name if you changed it from fax_number. The message limit prevents the browser from sending more text than Qubeforms retains for one string field.
<link rel="stylesheet" href="/static/assets/css/form-helper.css">
<form action="https://api.qubeforms.io/v1/cf_pk_YOUR_KEY/submissions" method="post">
<label for="contact-email">Email (required)</label>
<input id="contact-email" name="email" type="email" autocomplete="email" required>
<label for="contact-message">Message (required)</label>
<textarea id="contact-message" name="message" maxlength="20000" required></textarea>
<input class="form-helper" name="fax_number" type="text" tabindex="-1" autocomplete="off" aria-hidden="true">
<button type="submit">Send</button>
</form>4. Choose where visitors go after sending
Without a requested redirect, the browser displays the API response after submitting. To show your own thank-you page, allow its URL in Delivery & spam → Safe redirect allowlist and add a hidden _cf_redirect input with that URL. Merely saving the allowlist does not pick a destination. See Safe thank-you redirects for a complete example. Use AJAX and JSON submissions if you want to show feedback in the same page.
5. Connect notification email
Open Settings → Recipients, add the mailbox that should receive notifications, and complete verification. In Delivery & spam, keep Reply-To field set to email for this example. The recipient receives the notification; Reply-To identifies the visitor when you reply. Neither the visitor’s address nor your profile email automatically becomes a verified recipient.
6. Test from your actual website
Serve the page over HTTP or HTTPS and submit non-sensitive test data. The browser’s origin must be allowed for this form, including its scheme, host, and port. Check the stored fields in Submissions, look in Spam if needed, and inspect delivery status. A successful response confirms receipt, not that an email already reached the mailbox.
7. Extend the form
Add checkboxes and grouped fields using Field names, checkboxes, and multiple values. Add file inputs with enctype="multipart/form-data" on Starter or higher. If you enable CAPTCHA, integrate its widget and response token before publishing. Keep visitor input available when a request fails; native submission navigates away, so AJAX is useful when you need controlled error recovery in the page.
Public form keys can appear in browser code. Read keys, management credentials, and webhook signing secrets must stay on the server.