Forms
A contact form on a static site, with no backend and no script: the form posts to Sitebin, and Sitebin emails each submission to one recipient, who agreed to receive them.
SITEBIN_FORMS_SMTP_HOST is set — see
Self-hosting.Add a form
On the site's edit page, open Forms and give the form a name and a recipient. The name is what the recipient sees as the sender of every message. The recipient gets one email to confirm; until they click it, the form refuses submissions. That confirmation is what keeps a form from ever mailing someone who did not ask for it.
Then copy the form's snippet into any page of the site:
<form action="/_sitebin/forms/k7f3m2q9xaw4npd6" method="post"> <label>Name <input name="name" required></label> <label>Email <input name="email" type="email" required></label> <label>Message <textarea name="message" required></textarea></label> <input name="_gotcha" tabindex="-1" autocomplete="off" aria-hidden="true" style="position:absolute;left:-9999px"> <button type="submit">Send</button> </form>
The form posts to its own site — the site's address or your custom domain — so there is nothing to allow and nothing to load. Style it and add fields as you like: every field is forwarded, in the order the form has them.
Fields with a meaning
| Field | What it does |
|---|---|
email | Becomes the mail's Reply-To, so answering the mail answers the visitor. Only when it holds exactly one address, and not when that address is in the recipient's own domain: mail filters such as Microsoft 365 treat that as phishing, so the address then only appears in the message. |
_subject | The mail's subject (one line, up to 200 characters). Without it: New message via <form name>. |
_gotcha | A honeypot, moved off-screen. People never fill it; bots do. A submission with it filled is answered like a success and sends nothing. |
_… | Any field starting with an underscore is for you and the form, and is never forwarded. |
Captcha
Switch on Captcha and the snippet gains two lines: an ALTCHA widget and its script. The visitor's browser solves a small proof-of-work puzzle — no images, no tracking, no third party: the challenge and the script come from Sitebin itself. A solution is valid for five minutes, for this form only, once.
<altcha-widget challenge="/_sitebin/forms/k7f3m2q9xaw4npd6/challenge"></altcha-widget> <script type="module" src="/_sitebin/altcha.js"></script>
If your page sets its own Content-Security-Policy, allow
worker-src blob:: the widget solves in web workers.
Attachments
Switch on Attachments and the form posts
multipart/form-data with a file field. Up to 5 files of
2 MB each travel with the mail. Executable files
(.exe, .js, .bat, .msi
and the like) are refused: mail providers reject whole messages over them.
What arrives
One email per submission, from the form's name, with an HTML and a
plain-text part that lay out the fields in order, the attachments, and a
stop link. Every mail also carries submission.json, for
anything that reads mail by machine:
{
"version": 1,
"form": { "key": "k7f3m2q9xaw4npd6", "name": "Contact" },
"site": { "id": "…", "host": "www.example.com" },
"submitted_at": "2026-09-24T10:15:00Z",
"fields": [
{ "name": "name", "value": "Anna Muster" },
{ "name": "email", "value": "anna@example.com" },
{ "name": "message", "value": "Hello!" }
],
"files": [
{ "field": "cv", "filename": "cv.pdf", "content_type": "application/pdf",
"size": 183244, "sha256": "…" }
]
}
fields is a list, so repeated names (a group of checkboxes)
and the order survive. The visitor's IP address is not included.
Sitebin keeps no copy: a submission is mailed and gone.
After sending
Without JavaScript the browser lands on the form's thank-you page — a
path on the same site you set, such as /thanks.html — or a
plain default page. If something is wrong (the form is not confirmed yet,
a file is too large, too many messages in a short time), the visitor sees
why, with a way back.
Submitting with your own fetch? Send
Accept: application/json and get {"ok":true}, or
{"error":"…"} with the status: 403 for a form that
is not active or a failed captcha, 413 for too large,
429 for too many, 502 when the mail could not be
sent — try again.
The recipient decides
Every mail has a Stop emails from this form link, and mail clients show their own unsubscribe button for it. A stopped form refuses submissions until the site's owner resends the confirmation and the recipient accepts again. Changing the recipient asks the new address first.
Limits
- Forms per site: Pro 1, Studio 10. When a plan shrinks, the newest forms pause — nothing is deleted — and resume when the plan allows again.
- 10 submissions per visitor per hour across all forms, and 60 per form per hour.
- 50 fields of up to 10,000 characters; 5 files of 2 MB.
API and agents
Forms are managed with the site's edit password or an account token:
GET/POST /api/sites/{edit_id}/forms,
PUT/DELETE …/forms/{key}, and
POST …/forms/{key}/confirmation to resend — see the
API. Agents have the same through
MCP: list_forms, add_form,
update_form, remove_form and
resend_form_confirmation.
Self-hosting
Forms are open source, in every edition. Point
SITEBIN_FORMS_SMTP_HOST at an SMTP server and set
SITEBIN_FORMS_SMTP_FROM to the address mail leaves from; each
form supplies the display name. This mailer is separate from the one that
sends account mail. All variables are on the
configuration page.
With accounts enabled, a tier also needs "trusted": true to
get forms at all: an untrusted site is served with
form-action 'none', which leaves a plain form nothing to post
to. See Enterprise setup for the tier
fields.