Developer docs
One endpoint per form. Five ways in: an HTML form action, JSON, the sl.js embed, platform webhooks, or email. Everything lands in your connected Google Sheet.
Quick start: plain HTML
Point any form at your endpoint. No JavaScript required.
The browser is 303-redirected to your form's redirect URL (or a hosted thank-you page). Field names that match your sheet's column headers map automatically - "Email", "email", and "E-mail" all match an "Email" column. The hidden _slhp input is the spam honeypot: keep it in, leave it empty.
JSON API
Requests with Content-Type: application/json or Accept: application/json get a JSON response instead of a redirect. Multipart is accepted too (file parts are dropped in the current release). Bodies are capped at 256 KB. Errors: 404 unknown token, 410 paused form, 403 origin not allowed or challenge failed, 413 too large, 429 rate limited (per-form and per-IP), 400 unreadable body. Delivery to the sheet is asynchronous with automatic retries (5 min, 30 min, 2 h).
sl.js embed
Add once per page. It binds any <form data-sheetlink> and upgrades any form whose action already points at your endpoint:
- AJAX submit with inline success/error text - no page reload
- Ad attribution:
utm_*,gclid,wbraid,gbraid,fbclid,msclkidcaptured from the URL and persisted across pages - add matching columns to receive them - Page URL, referrer, and a bot-timing signal (stored as submission metadata)
- Honeypot injected automatically
- SPA-safe: late-rendered forms are bound via MutationObserver
Form attributes: data-sheetlink="slf_..." (bind + per-form token), data-redirect (navigate on success), data-success-message (inline text). Events: sheetlink:success and sheetlink:error bubble from the form with details in event.detail.
Webflow & Framer webhooks
Webflow: Site settings -> Integrations -> Webhooks -> Add webhook, trigger type "Form submission", paste the URL - one webhook covers every form on the site, and the originating form name is stored with each submission. Framer: set the Form component's submission destination to Webhook and paste the URL. Both v2 and legacy Webflow payloads are handled.
Email-in
The catch-all: point any system's notification email at your form's address and lines shaped Label: value become fields; when nothing parses, the subject, sender, and body are stored so the lead is never lost.
For IT admins: Microsoft 365 approval
If your organization requires admin approval for new apps, an Entra admin can grant SheetLink org-wide consent in one step - after that, users connect their Microsoft account without an approval prompt:
What you are approving: app "SheetLink", application (client) ID 27b7f757-de78-4806-a92a-c4e50572a825. Delegated permissions only - Files.ReadWrite (write rows to the workbook the user picks), User.Read (show which account is connected), offline_access (keep delivering while the user is offline). No application-level (app-only) permissions, no admin data access. All SheetLink traffic to Microsoft originates from the static egress IP 69.197.187.14 if you allowlist outbound integrations. Refresh tokens are stored AES-256-GCM encrypted and are revocable at any time from your tenant's Enterprise applications blade or the SheetLink dashboard.
Code snippets
fetch (browser or edge)
Node.js
PHP
curl
Spam handling & control fields
Every submission passes rate limits, an origin allowlist (when configured), the honeypot, and content heuristics tuned by your form's strictness dial. Suspicious submissions are quarantined for review in the dashboard, never silently dropped - approving one delivers it. Reserved field names (stripped from your sheet, used by the pipeline):
| Field | Purpose |
|---|---|
_slhp | Honeypot - must be present and empty; bots that fill it are marked spam |
_sl_elapsed_ms | sl.js timing signal (load-to-submit) |
_sl_page | Submitting page URL (stored as metadata) |
_sl_referrer | Page referrer (stored as metadata) |
cf-turnstile-response | Cloudflare Turnstile token, when the challenge is enabled |