One endpoint per client site
A form per client, an allowlist per domain, a sheet the client can watch, and a quarantine you review as a service. Lead capture becomes a line item.
The workflow
Agencies do not have a form; they have twenty, spread across client sites built in whatever each project called for. The failure mode is uniformity in the wrong place - one shared account, one tangle of notification emails, and no way to answer "did my form break?" for a specific client without archaeology.
The pattern that scales: one SheetLink form per client site. Each form has its own permanent endpoint token, its own destination sheet, its own origin allowlist, its own spam settings, and its own delivery log. The client's site posts to its endpoint; rows land in a sheet you share with the client; nothing about client A's configuration can touch client B. The pipeline every form rides is on how it works.
Sites on Webflow or Framer skip markup entirely: point the platform's form webhook at /w/webflow/your_token or /w/framer/your_token. One Webflow webhook covers every form on the site, with the form name recorded per row. Client on WordPress? The sibling plugin at sheetlinkwp.com covers that; this page is about everything else.
Locked to their domain
The endpoint token sits in page source, which means anyone can read it. The per-form origin allowlist is what makes that safe at agency scale: set each form's allowlist to that client's domain and the endpoint rejects posts from anywhere else. A token scraped from client A's site cannot be replayed from a spammer's page, and cannot be pointed at from client B's staging site by accident either.
The same per-form isolation applies to rate limits - a spam storm against one client's form throttles that form, not your whole roster - and to lifecycle: when a client offboards, pause their form and the endpoint stops accepting, cleanly, without touching anything else. When a client rebrands onto a new domain, the fix is editing one allowlist entry.
Suggested columns
Per client sheet, the shape is familiar:
- Name, Email, Phone, Message or Service - whatever their form asks.
- utm_source, utm_campaign, gclid - for clients whose ads you run, these columns are the monthly report writing itself. The
sl.jsembed captures and persists them; the mechanics are in capture gclid in forms. - Submitted At - the column that settles "we never got any leads this month" conversations with timestamps.
- Status - a working column the client's own team fills in, which quietly tells you whether they are following up on the leads you deliver.
The sheet is a normal Google Sheet or Excel workbook: share it with the client read-only or editable, their choice of involvement. The destination stays theirs to watch even though the plumbing is yours to run.
Quarantine review as a service
Spam handling is usually an apology agencies make on behalf of their form stack. Here it is billable structure. Suspicious submissions across every client form are quarantined - held for one-click review, never silently dropped - so a weekly sweep of your clients' quarantines is a real deliverable: "we review every flagged submission by hand; no lead is ever silently discarded on your site." Approve delivers the row; the false positive costs seconds.
The delivery log is the other half of the service story. When a client says the form is broken, you answer with timestamps: submission received at 14:02, delivered to the sheet at 14:02, retried once at 14:07 during a Google API blip. Retries run automatically at 5 minutes, 30 minutes, and 2 hours, so "broken" is almost always "already handled", and now you can show it.
The rollout pattern
Same block on every custom-built site, with the client's own token:
<form action="https://sheetlinkforms.com/f/client_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>
<script src="https://sheetlinkforms.com/sl.js" data-token="client_token"></script>The honeypot ships in the markup on every site, no exceptions; the embed adds AJAX submit, attribution capture, and SPA support for the sites built in frameworks. Response codes and the JSON API - useful when a client project has a custom frontend - are in the docs.
FAQ
How many client forms can one account run?
As many as you have clients - forms are cheap to create and each is independently configured. The beta is free with an invite from the waitlist, and there are no per-submission fees, so a busy client site costs the same as a quiet one.
Who owns the lead data?
The destination sheet is a normal spreadsheet file - share it with the client, move it to a shared drive, or hand over ownership like any other file. The Google connection is drive.file scoped, so SheetLink only ever touches the specific sheets you picked.
What happens when a client changes domains?
Edit that form's origin allowlist to the new domain. Nothing else changes - the endpoint token is permanent, so the site-side markup keeps working through redesigns and migrations.
What about client sites on Webflow or Framer?
Use the platform webhooks instead of markup: Webflow's Site settings -> Integrations -> Webhooks with the "Form submission" trigger pointed at /w/webflow/your_token (one webhook covers all forms on the site, form name recorded), and Framer's Form component webhook destination pointed at /w/framer/your_token.
Related: Quote requests your team can actually work · Lead capture for ad landing pages · A contact form that lands in a sheet