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:
- Name, Email, Message - the fields your form sends. Field names match headers case- and punctuation-insensitively, so
emaillands in an Email column with no configuration. - Submitted At - when the row landed. This is your queue order.
- utm_source - optional. If the page loads the
sl.jsembed, utm parameters are captured from the URL and persisted across pages, so you can tell a campaign enquiry from an organic one. - 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:
- Make a filter view where Status is empty. That view is your inbox, and unlike an inbox it empties permanently.
- 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.
- Reply from your mail client, then write the date into Replied On. Sorting by that column occasionally is a humbling exercise.
- 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