Documentation

Connect a form in two minutes

Scroll Theory Forms gives any HTML form a backend. You point your form at an endpoint, and submissions are stored, spam-filtered, and emailed to you. No JavaScript required, and nothing to install.

Quick start

Three steps:

  • Create a form in your dashboard. You get a unique endpoint.
  • Set your HTML form's action to that endpoint and its method to POST.
  • Submit it. The submission appears in your dashboard, and if you have a verified notification email, it lands in your inbox.

The endpoint

Every form has an endpoint shaped like this:

POST https://forms.scrolltheory.media/f/YOUR_FORM_ID

It accepts three body types, so it works with plain HTML forms and with JavaScript:

  • URL-encoded (a normal HTML form post).
  • Multipart (a form with a file input; files are recorded by name in this version).
  • JSON (an AJAX request, see AJAX and JSON).

HTML forms

A complete contact form. Replace YOUR_FORM_ID with the id from your dashboard.

<form action="https://forms.scrolltheory.media/f/YOUR_FORM_ID" method="POST">
  <input type="email" name="email" required>
  <textarea name="message" required></textarea>

  <!-- optional: set the email subject -->
  <input type="hidden" name="_subject" value="New lead from your site">

  <!-- spam honeypot: keep it hidden, leave it empty -->
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off"
         style="position:absolute;left:-9999px" aria-hidden="true">

  <button type="submit">Send</button>
</form>

That is the whole integration. No library, no API key in the page.

Field names

Use any field names you like. Whatever you send is stored and shown in your dashboard, and emailed to you. Common names like name, email, and message work well, and a field named email is automatically used as the reply-to address on your notification unless you set one explicitly.

If you plan to use routing rules, you can declare your field names when you create the form. That lets rules reference them before the first submission arrives. You can always add more later.

Control fields

A few reserved field names change how a submission is handled instead of being stored as data. They are prefixed with an underscore and are compatible with Formspree, so an existing form keeps working.

FieldWhat it does
_replytoSets the reply-to address on your notification email. Defaults to a field named email if present.
_subjectSets the subject line of your notification email.
_nextRedirects the visitor to this URL after a successful submit. Overrides the form's default redirect.
_ccAdds a carbon-copy address to the notification.
_gotchaHoneypot. Keep it hidden and empty. If a bot fills it, the submission is quarantined as spam.
_formatHints the response format.

Captcha tokens (cf-turnstile-response, g-recaptcha-response, h-captcha-response) are detected automatically and never stored as form data.

Redirects and the thank-you page

After a normal form submit, one of three things happens, in this order:

  • If a routing rule sets a redirect, the visitor goes there.
  • Otherwise, if the submission includes _next or the form has a default redirect URL, the visitor goes there.
  • Otherwise, a clean built-in thank-you page is shown.

To send visitors to your own page, add a hidden field:

<input type="hidden" name="_next" value="https://yoursite.com/thank-you">

AJAX and JSON

For single-page apps or custom success states, post JSON and set the Accept header to application/json. The endpoint replies with JSON instead of redirecting.

const res = await fetch("https://forms.scrolltheory.media/f/YOUR_FORM_ID", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify({ email, message })
});

const data = await res.json();
// { ok: true, id: 123, spam: false, limited: false }
if (data.ok) {
  // show your own success state
}

A successful response looks like { ok: true, id, spam, limited }. The spam flag tells you the submission was quarantined, and limited tells you the account is over its monthly cap so no email was sent. The submission is still stored either way. A failure returns { ok: false, error } with an appropriate status code.

Cross-origin requests are supported, so you can post from your own domain.

Spam handling

Spam is quarantined, not deleted. A flagged submission is still stored and visible in your dashboard, so a false positive never costs you a real lead. This is the core difference from filters that silently drop messages.

The checks run in order: the honeypot field, an optional Cloudflare Turnstile challenge, a rate limit on bursts from one source, and your blocklist of addresses, IPs, or keywords. Whatever trips first marks the submission as spam and stops the rest.

Notifications and verified emails

To receive email when a form is submitted, add a notification address and verify it from the Emails page. Verification exists so the service cannot be used to send mail to addresses you do not control. You choose which verified address each form notifies, and routing rules can send different submissions to different verified addresses.

Plans and limits

Every plan stores every submission. When an account goes over its monthly submission cap, email notifications pause, but submissions keep being recorded and stay available in the dashboard. Upgrading restores notifications. Current plans and limits are on the pricing section.

Moving from Formspree

The control fields above are Formspree-compatible. In most cases you change the form's action URL to your Scroll Theory Forms endpoint and the form keeps working, including _replyto, _subject, _next, and _gotcha. The difference you will notice is that flagged messages are held for review instead of disappearing.

Questions about anything here can go to chad@scrolltheory.media.