Your site is made of static files, so a contact form on it has nowhere to send its answers. Zeloxa gives every form an address to send to. Visitors fill in the form as usual, and each message lands in your project's Forms tab and, if you like, in your email. Spam is filtered out for you.
It works with any site: plain HTML, a site builder export, or a framework. There is no code to run on a server.
Add a contact form
- Open your project in Hosting and choose the Forms tab.
- Choose Create your first form (or New form).
- Give it a Form name you will recognise, such as
Contact form. - Under Email new submissions to, tick the people in your organization who should get each message (up to 5; you are ticked already). Untick everyone to only collect messages in the dashboard.
- Choose Create form.
What you'll see: the form's Add to your site page, with its address, a ready-made contact form to copy, and a test command.
- Copy the HTML with the Copy button and paste it into the page where the
form should appear (for example
contact.html). - Deploy your site as usual, open the page, and send yourself a test message.
What you'll see: a "Thank you!" page with a Back to site button. The message appears under Submissions, and in your email if you added an address.
The ready-made form
This is the form the dashboard gives you, with your own form's address in
place of f_XXXXXXXXXXXXXXXX:
<form action="https://zeloxalabs.com/api/forms/f_XXXXXXXXXXXXXXXX" method="POST">
<label>Name <input type="text" name="name" required></label>
<label>Email <input type="email" name="email" required></label>
<label>Message <textarea name="message" rows="5" required></textarea></label>
<input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">
<button type="submit">Send</button>
</form>Change it to suit your site:
- Add or remove fields. Every field with a
nameis saved, in the order it appears in the form. Checkboxes and multiple choices that share a name are saved together, separated by commas. - Style it with your own CSS. Only the
actionandmethodmatter. - Already have a form? Keep it, and set its
actionto your form's address and itsmethodtoPOST. - Keep the hidden
_gotchafield. It is the spam trap: people never see it, bots fill it in (see Spam protection). - Name the email field
email. Notification emails then reply straight to the person who wrote.
File uploads are not supported: file fields are ignored, and a very large upload is refused. Ask visitors for a link instead (for example to a shared folder).
Send it with JavaScript
To keep visitors on the page and show a message instead of a thank-you page,
send the form with fetch. The Add to your site page has a complete,
copyable example; the important part is:
const response = await fetch("https://zeloxalabs.com/api/forms/f_XXXXXXXXXXXXXXXX", {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});
const result = await response.json();You can also send JSON, for example from a framework:
await fetch("https://zeloxalabs.com/api/forms/f_XXXXXXXXXXXXXXXX", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Ada", email: "ada@example.com", message: "Hello" }),
});What you'll see: status
200and{ "ok": true, "message": "Thanks! Your submission was received." }. When something is wrong, the status explains it and the body says why, for example{ "ok": false, "error": { "code": "RATE_LIMITED", "message": "…" } }.
Browsers only let a page read the answer when the page is on your own site: your project's address, its preview addresses and its connected custom domains, over HTTPS. A page anywhere else can still send the form, but cannot read the reply.
| Status | error.code | What it means |
|---|---|---|
| 400 | EMPTY_SUBMISSION | Every field was empty. |
| 400 | INVALID_BODY | The JSON or form data could not be read. |
| 400 | CHALLENGE_FAILED | The anti-spam check is on and was not completed. |
| 403 | FORM_UNAVAILABLE | The form is turned off, or the account is suspended. |
| 404 | NOT_FOUND | No form has this address (it may have been deleted). |
| 413 | TOO_MANY_FIELDS | More than 50 fields. |
| 413 | FIELD_TOO_LARGE | One field is longer than 10 KB. |
| 413 | PAYLOAD_TOO_LARGE | The whole submission is larger than 64 KB, or a file was attached. |
| 415 | UNSUPPORTED_CONTENT_TYPE | The body was not form data or JSON. |
| 429 | RATE_LIMITED | Sent too often. Wait for the time in the Retry-After header. |
| 429 | MONTHLY_LIMIT | The project received its monthly maximum of submissions. |
A normal browser form post gets the same answers as a simple page instead of JSON, with a Back to site button when the form was on your site.
Thank-you page
After sending, visitors see a plain "Thank you!" page with a Back to site button. It shows no name or logo, so it suits any site.
To use a page of your own instead, open the form's Settings and fill in Thank-you page:
- a path on your site, such as
/thanksor/thanks.html(the visitor stays on the domain they were on), or - a full address on one of your project's own domains, such as
https://www.yourbusiness.com/thanks.
Addresses on other websites are refused, so the form cannot be used to send visitors somewhere unexpected. If you later remove the domain the page is on, visitors get the plain thank-you page instead.
Email notifications
Each new message (not spam) is emailed to the people ticked under Email new
submissions to. The email lists every field, links to the message in your
dashboard, and replies go to the visitor when the form has a valid email
field.
Notifications only go to members of your organization: the list shows your team, and the person who creates a form is ticked by default. To email someone else, invite them to the organization first. Recipients are checked again for every message, so someone who leaves the organization stops receiving its submissions straight away, even before the form is edited.
An email that cannot be delivered never loses the message: it is always saved in the inbox first. Each message shows whether its email went out (Emailed) or not, and why.
Spam protection
Every form is protected in four ways, with nothing for you to set up:
- The spam trap. The hidden
_gotchafield is invisible to people, but bots fill in every field they find. Anything that fills it in is accepted quietly (so the bot does not retry) and filed under Spam. If your form already uses a field called_gotcha, choose another name under Spam protection in the form's settings and use that name in your HTML. - Link filter. A message that is little more than links, or has more than five, is filed under Spam.
- Rate limits. One visitor can send a form 10 times a minute, and one form accepts 500 submissions a day. From the 21st message in a day, everything one visitor sends to a form is accepted as usual but filed under Spam: it is never emailed and does not count towards the monthly limit, so one sender cannot fill your inbox or use up your month.
- Size limits. See Limits.
Spam is kept for you to check, in the Spam folder, and is never emailed. If something real ends up there, open it and choose Not spam.
For forms that attract a lot of spam, turn on Require an anti-spam check under Spam protection, when your account offers it. Visitors then pass a quick check, usually invisible, before the form sends. The Add to your site page shows the two extra lines to add to your page; without them, submissions are refused.
Your inbox
The Submissions tab of a form lists messages newest first, 25 per page.
- Open a message to read every field. Opening it marks it as read; choose Mark as unread to flag it again. Unread only shows what you have not read yet.
- Mark as spam moves a message to the Spam folder; Not spam moves it back.
- Delete removes a message for good, after you confirm.
- Export downloads a CSV file that opens in Excel, Numbers or Google
Sheets: the folder you are looking at, or Export all. Values that a
spreadsheet would treat as a formula (starting with
=,+,-or@) are saved with a leading apostrophe, so opening an export can never run something a visitor typed.
To stop a form from accepting messages without deleting anything, choose Turn off. Visitors who send it then see that it is not accepting submissions. Turn on reverses it.
Deleting a form (under Settings) deletes all of its messages too, and its address stops working at once.
Limits
| Limit | Value |
|---|---|
| Forms per project | 20 |
| Notification addresses per form | 5 |
| Fields per submission | 50 |
| Size of one field | 10 KB |
| Size of one submission | 64 KB |
| Submissions per visitor | 10 per minute, per form |
| Inbox messages per visitor | 20 per day, per form (the rest go to Spam) |
| Submissions per form | 500 per day |
| Submissions per project | 1,000 per month |
| How long submissions are kept | 12 months |
The monthly limit counts inbox messages across all of a project's forms, from the 1st of each month (UTC). Spam does not count towards it. The Forms tab shows how much of it is used. Once it is reached, new messages are refused until the next month, and visitors are asked to get in touch another way.
Privacy
- Messages are stored for 12 months, then deleted automatically. Export them to keep a copy for longer.
- Visitors' IP addresses are never stored. Only a one-way code made from the address is kept, so repeated sending can be limited without knowing who sent it.
- The browser name and the page the form was sent from are saved with each message, to help you spot spam.
- Only members of your organization can see a project's messages.
Troubleshooting
"This form does not exist." The address in your form's action is wrong
or the form was deleted. Copy the address again from the form's Add to your
site page.
"This form is not accepting submissions right now." The form is turned off. Open it and choose Turn on.
Messages arrive in Spam. Check the hidden field: it must stay empty and
hidden (style="display:none"). A form builder that fills in every field, or
a password manager, can trigger the trap; rename the field in Spam
protection if needed.
No email arrives. Check who is ticked under Email new submissions to (only members of your organization can be emailed) and your spam folder. The message itself is always in the dashboard, with its email status.
My JavaScript cannot read the reply. The page must be on your project's own domain over HTTPS. Test from your live site rather than from a file on your computer.
I get "Please wait a moment". The same browser sent the form more than 10 times in a minute. Wait a minute and try again.
For developers: the dashboard API
The Forms tab uses these routes. They take the dashboard session only (not
API tokens) and live under /api/host/sites/{siteId}:
| Method | Path | Does |
|---|---|---|
| GET | /forms | Lists forms with this month's counts and the project's usage. |
| POST | /forms | Creates a form: name, notifyEmails (members' addresses only; defaults to yours), redirectUrl, honeypotField, requireChallenge, enabled. |
| GET | /forms/{formId} | One form. |
| PATCH | /forms/{formId} | Changes any of the fields above. |
| DELETE | /forms/{formId} | Deletes the form and its submissions. |
| GET | /forms/{formId}/submissions?folder=inbox&unread=1&page=1 | One page of submissions, newest first. |
| PATCH | /forms/{formId}/submissions/{submissionId} | { "read": true } or { "spam": false }. |
| DELETE | /forms/{formId}/submissions/{submissionId} | Deletes one submission. |
| GET | /forms/{formId}/export?folder=inbox | CSV of inbox, spam or all (10,000 newest at most). |
Creating, changing and deleting forms, and deleting submissions, are recorded in Activity.
