SheetLink Forms beta

A contact form that lands in a sheet

Name, email, message. One endpoint, one sheet, and every enquiry becomes a row you can sort, assign, and mark answered.

The workflow

A contact form is the smallest useful form on the internet, and it is usually wired to the least useful destination: an inbox. Messages arrive, get read, get buried. Three weeks later nobody can say which ones were answered.

The spreadsheet version is simpler and keeps records. Your form's action points at a permanent endpoint - https://sheetlinkforms.com/f/your_token - and every submission is screened for spam, mapped to columns, and appended as a row in a Google Sheet or an Excel Online table. Delivery goes straight to the Google Sheets API or Microsoft Graph; there is no middleman service relaying your data. The sheet becomes the working system: filter it, add a Status column, and every enquiry has a paper trail.

The full pipeline is described on how it works, and there is a live demo posting into a public sheet if you want to watch a row land before you build anything.

Suggested columns

Put a header row on the sheet (or let SheetLink seed one on an empty sheet) along these lines:

  1. Name, Email, Message - the fields your form sends. Field names match headers case- and punctuation-insensitively, so email lands in an Email column with no configuration.
  2. Submitted At - when the row landed. This is your queue order.
  3. utm_source - optional. If the page loads the sl.js embed, utm parameters are captured from the URL and persisted across pages, so you can tell a campaign enquiry from an organic one.
  4. Status and Replied On - working columns you fill in by hand. Submissions never touch them; they are what turns a log into a queue.

The form markup

This is a complete contact form. No JavaScript, no build step, no API key in the page:

<form action="https://sheetlinkforms.com/f/your_token" method="POST">
  <input name="name" placeholder="Name" required>
  <input name="email" type="email" placeholder="Email" required>
  <textarea name="message" placeholder="How can we help?"></textarea>
  <input name="_slhp" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">
  <button type="submit">Send</button>
</form>

The hidden _slhp input is the honeypot: humans never see it, bots that fill every field do, and those submissions are marked spam outright. After a plain HTML submit the visitor gets a 303 redirect to a hosted thank-you page or a redirect URL you set. If you would rather keep them on the page with an inline success message, add one line:

<script src="https://sheetlinkforms.com/sl.js" data-token="your_token"></script>

Details on both modes, plus the JSON API, are in the developer docs.

Working the sheet

A few habits make the sheet a genuinely better tool than the inbox it replaced:

  1. Make a filter view where Status is empty. That view is your inbox, and unlike an inbox it empties permanently.
  2. Add conditional formatting that colors rows where Submitted At is more than two days old and Status is still blank. Response-time problems become visible instead of anecdotal.
  3. Reply from your mail client, then write the date into Replied On. Sorting by that column occasionally is a humbling exercise.
  4. Never delete rows. A years-long, searchable record of every enquiry is the quiet payoff of this setup.

Delivery is asynchronous with retries at 5 minutes, 30 minutes, and 2 hours if Google or Microsoft has a bad moment, and every attempt is visible in the delivery log. Cell values that start with =, +, -, or @ are escaped, so a message beginning with a formula cannot execute in your sheet.

Spam considerations

Contact forms are the most-spammed form type on the web, which is exactly why silent filtering is dangerous: the day the filter guesses wrong, a real customer vanishes.

SheetLink layers its defenses. The honeypot catches crude bots outright. Rate limits per form and per IP absorb floods. Content heuristics - link stuffing, disposable email domains, empty payloads - run at a strictness you set per form; normal is the right default for a contact form. Anything merely suspicious is quarantined for one-click review, never dropped: approve it and it delivers to the sheet. A false positive costs you seconds, not a lead. If volume gets ugly, enable Cloudflare Turnstile on the form. The full playbook is in the spam protection guide.

FAQ

Do I need JavaScript for this to work?

No. A plain HTML form posting to the endpoint works with zero scripts - the visitor gets a 303 redirect to a thank-you page. The sl.js embed is optional and adds AJAX submit with inline success text, plus utm capture.

Can rows go to Excel instead of Google Sheets?

Yes. Connect a Microsoft 365 account and pick an Excel table; rows append into it and the table's columns define the row shape. Google Sheets and Excel Online are both first-class destinations.

What if my sheet is empty when the first message arrives?

SheetLink seeds a header row on empty sheets, then appends rows under it. If you already have headers, incoming field names map to them automatically - "Email", "email", and "E-mail" all match an Email column.

What does this cost?

SheetLink Forms is free during the beta - an invite from the waitlist is all you need. There are no per-submission fees, and there never will be.

Related: Quote requests your team can actually work · Feedback and NPS into a living sheet · Forms for side projects and hackathons

Request an invite See the live demo