Forms to Google Sheets
Point any form at a permanent endpoint and every submission lands as a row in a sheet you picked - connected in one click, scoped to only the sheets you choose, never the rest of your Drive.
The spreadsheet is the working system
Most form tools stop at the inbox. A submission arrives as an email, someone forwards it, someone else retypes it into a sheet, and by Thursday two versions of the truth exist. SheetLink Forms treats the spreadsheet as the destination, not an afterthought: an asynchronous worker writes each accepted submission straight to the Google Sheets API - no Zapier, no middleman task queue, no per-submission fees.
That makes the sheet itself the working system. The people who follow up on leads, RSVPs, or applications already live in Google Sheets. Rows land where the work happens, in the columns the team already uses. If you want to see the full pipeline - endpoint, screening, delivery - the how it works page walks through every step, and the live demo writes into a public sheet you can watch.
One-click connect, scoped to only the sheets you pick
Connecting Google Sheets is a single OAuth click, and the connection uses Google's drive.file scope. That scope is deliberately narrow: SheetLink Forms can only see spreadsheets you explicitly pick in the Google picker or spreadsheets it creates for you inside the product. It cannot list, read, or touch anything else in your Drive - not your documents, not your folders, not other spreadsheets.
This is the opposite of the broad "see and manage all your files" grant that many integrations ask for. The permission boundary is enforced by Google, not by a promise in our terms: if you did not pick a sheet, the connection has no access to it. Revoking is equally clean - disconnect, and the grant is gone.
Setup is: connect Google, then either pick an existing spreadsheet in the picker or let the product create a fresh one. That sheet becomes the destination for the form's endpoint at https://sheetlinkforms.com/f/{token}.
Header seeding and append-under-header
A destination sheet needs a header row, because headers are what field names map against. If you point a form at an empty sheet, SheetLink Forms seeds the header row for you from the form's fields - you do not have to type column names before the first submission arrives.
From then on, every delivered submission appends as a new row under that header. Row 1 stays the header; data grows downward in submission order. Nothing is overwritten and nothing is inserted into the middle of your data, so formulas that reference whole columns, filters, and pivot tables keep working as the sheet fills.
If you prefer to design the header yourself - reorder columns, add columns your form does not send, pre-build totals - that works too. Set up the header row first, and incoming fields map against it.
Auto-mapping by column name
Field names that match your column headers map automatically, and the match is case and punctuation insensitive: a field named email, Email, or E-mail all land in an "Email" column. In practice this means most forms need zero mapping configuration - name your inputs the way you named your columns, roughly, and rows assemble themselves.
When names genuinely differ - a legacy form that posts fname into a "First Name" column - explicit per-field mapping is available, so you can pin any incoming field to any column. Auto-mapping covers the common case; explicit mapping covers the rest. Columns with no matching field simply stay empty for that row, which is exactly what you want when several forms with different fields feed related sheets.
What a submission looks like as a row
Take a plain HTML form pointed at your endpoint:
<form action="https://sheetlinkforms.com/f/YOUR_TOKEN" method="POST">
<input name="Name">
<input name="Email" type="email">
<textarea name="Message"></textarea>
<input name="_slhp" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">
<button>Send</button>
</form>A visitor submits, and one row appears: Name in the Name column, Email under Email, Message under Message, appended under the header. The _slhp input is the honeypot - invisible to people, filled by bots, and the one signal that marks spam outright. Everything else suspicious is quarantined for one-click review, never silently dropped.
Cell values are also guarded against formula injection: leading =, +, -, and @ characters are escaped before they reach the sheet, so a malicious submission cannot plant a live formula in your data. Full request formats - urlencoded, JSON, multipart - are in the docs.
Attribution columns: know which click became which row
Add the sl.js embed to your site and each submission can carry its marketing origin with it. The script captures utm_source, utm_medium, utm_campaign, utm_term, and utm_content, plus the ad click IDs gclid, wbraid, gbraid, fbclid, and msclkid, from the URL - and persists them in localStorage across pages, so the visitor who arrived from an ad three pages ago still gets attributed when they finally submit.
Add matching columns to your sheet - a gclid column, a utm_campaign column - and those values land in the row alongside the form fields. The result is a lead sheet where you can group by campaign, count rows per source, and hand click IDs to your ads platform for offline conversion tracking, without any tag manager gymnastics. Very few form backends record ad click IDs at all; here it is a column like any other.
Delivery you can audit
Delivery is asynchronous and direct to the Google Sheets API. If Google is briefly unreachable or a write fails, the worker retries at 5 minutes, 30 minutes, and 2 hours, and the dashboard keeps a delivery log so you can see exactly which submission became which row and when. A submission is never lost between the form and the sheet - it is either delivered, retrying, or sitting in quarantine waiting for your one-click approve.
That approve-delivers model matters for spreadsheets specifically: when a quarantined submission turns out to be a real lead, approving it writes the row exactly as if it had passed screening the first time. A false positive costs seconds, not a lead.
Once the rows land, it is just Sheets
Because the destination is a normal Google Sheet, everything Sheets can do applies to your submissions with no export step. Filter views let each teammate slice leads without disturbing others. Pivot tables summarize submissions by day, campaign, or any column you collect. IMPORTRANGE can mirror the raw submission sheet into a dashboard spreadsheet, so the form writes to one tab and your reporting lives safely elsewhere. Conditional formatting flags rows that need follow-up; QUERY and FILTER build live views like "this week's leads from paid search."
None of that requires anything from us - it is the point of delivering to a spreadsheet instead of an inbox. Prefer the Microsoft side of the fence? The same pipeline delivers to Excel Online via Microsoft Graph. Either way, SheetLink Forms is free during beta - grab an invite from the waitlist.
FAQ
Can SheetLink Forms see the rest of my Google Drive?
No. The connection uses Google's drive.file scope, which limits access to spreadsheets you explicitly pick in the Google picker or spreadsheets created inside the product. Files you never picked are invisible to the connection - the boundary is enforced by Google's OAuth scope, not by policy.
Do I have to set up column headers before submissions arrive?
No. If the destination sheet is empty, SheetLink Forms seeds the header row from your form's fields automatically. If you would rather design the header yourself - custom order, extra columns for notes or formulas - build it first and incoming fields will map against it, automatically where names match and by explicit mapping where they do not.
What happens if a field name does not exactly match a column header?
Matching is case and punctuation insensitive, so email, Email, and E-mail all map to an "Email" column. For names that genuinely differ, explicit per-field mapping lets you pin any field to any column. Columns with no matching field stay empty for that row.
Do I need the sl.js embed for attribution columns?
Yes - the embed is what captures utm_* parameters and the ad click IDs (gclid, wbraid, gbraid, fbclid, msclkid) from the URL and persists them across pages until the visitor submits. Plain HTML posts still deliver the form fields; the embed adds the marketing origin. Setup details are in the docs.
What does a failed delivery look like?
Delivery is asynchronous with automatic retries at 5 minutes, 30 minutes, and 2 hours, and the dashboard keeps a delivery log per submission. Suspicious submissions are quarantined for one-click review - approving delivers the row - and are never silently dropped. You can watch a real submission become a row on the live demo.
How much does the Google Sheets destination cost?
SheetLink Forms is free during beta (invite required - join the waitlist), and there are no per-submission or per-task fees, ever. Post-beta plans are a planned free tier of roughly 50 submissions per month and an unmetered paid plan around $15-19/mo.