slew docs/forms/forms

Forms

Every site on slew accepts form submissions — static sites included, with no JavaScript and no backend. Point any HTML form at /_slew/forms/<name> on your own site:

<form action="/_slew/forms/contact" method="POST">
  <input name="email" type="email" required>
  <textarea name="message"></textarea>
  <input name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">
  <button>Send</button>
</form>

That's the whole integration. The form registers itself at the first submission — nothing to declare at build time, so a form rendered by JavaScript works exactly like one in static HTML. Submissions land in the console's Forms tab, and the project's owners get an email.

The endpoint is on your site's own origin (yoursite.com/_slew/forms/contact), so there is no CORS, no third-party domain in your HTML, and it works the same on custom domains, <project>.slew.cloud, and branch domains.

Forms are included in every plan. Submissions are stored in the EU, like everything else on slew.

What happens after submit

  • Browser (HTML) submissions are answered with a redirect: to the path in a hidden _next field if you add one (<input type="hidden" name="_next" value="/thanks">), else back to the page the form was on, else a minimal confirmation page. _next must be a path on your own site.
  • JSON clients (Content-Type: application/json, or an Accept: application/json header) get { "ok": true } — for fetch()-based forms:
await fetch('/_slew/forms/signup', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email }),
})

Field names starting with _ are reserved for form controls and never stored.

Spam

The _gotcha field in the example is a honeypot: hide it with CSS, and anything that fills it is answered with a fake success and stored nowhere. Submissions are also rate-limited per visitor address.

If a form still attracts junk, notifications can be switched off while you keep collecting, and individual submissions deleted from the console.

Notifications

New submissions are emailed to the project's owners (for an org project: every org owner) within a minute, batched — a burst of submissions is one email. Per form you can:

  • route notifications to a different address (a shared inbox, a client),
  • or turn them off entirely; submissions keep landing in the console.

Retention and GDPR

Form submissions are usually personal data. Slew stores them in the EU and gives you the controls a processor should:

  • Retention: set a per-form window (say 90 days) and older submissions are deleted automatically. No window = kept until you delete them.
  • Export: any form's full history as CSV, from the console or slew forms export.
  • Delete: individual submissions or the whole form, from the console, CLI or API.

No visitor IP addresses or user agents are stored with submissions — only the fields your form sent and the page path it was on.

Limits

Submissions per month Free 100 · Pro 1,000 · Scale 5,000 · Studio 20,000 (per account or org)
Forms per project 20
Fields per submission 100
Submission size 32 KB
File uploads Not stored — file inputs are ignored, the rest of the form goes through

At the monthly cap further submissions are refused with an explanatory page (they are stored data, not traffic — unlike bandwidth, which never hard-stops).

CLI

slew forms                      # list forms and submission counts
slew forms submissions contact  # recent submissions
slew forms export contact > contact.csv
slew forms notify contact clients@agency.example
slew forms retention contact 90
slew forms rm contact

HTTP API

Ingest is unauthenticated on your site's origin: POST /_slew/forms/<name> with application/x-www-form-urlencoded, multipart/form-data, or application/json.

Management, with Bearer auth like everything else:

Method Path Description
GET /projects/:name/forms The project's forms with submission counts
GET /projects/:name/forms/:form/submissions Submissions, newest first — ?limit= (max 200) and ?before=<timestamp> to page
GET /projects/:name/forms/:form/export Full history as CSV
PATCH /projects/:name/forms/:form Settings — { "notify"?, "notifyEmail"?, "retentionDays"? }
DELETE /projects/:name/forms/:form Delete the form and its submissions
DELETE /projects/:name/forms/:form/submissions/:id Delete one submission

Errors on ingest:

Code Meaning
invalid_form_name Names are lowercase letters, digits, - and _, at most 64 characters
empty_submission No storable fields (everything was reserved or missing)
too_large Over 32 KB of data
rate_limit_exceeded Too many submissions from one address — wait a few minutes
form_limit_reached The project already has 20 forms
form_over_quota The plan's monthly submissions are used up