A form submission is useful only when someone can find it and follow up. This guide designs a Make workflow that receives a form payload through a Custom webhook and records it in Google Sheets. A stable submission ID and a small processing register help control duplicate deliveries. This is a documentation-based walkthrough, not a workflow executed for this article. All sample input and expected results are synthetic.
Before you begin
Use this design for modest submission volumes and a form provider that can send authenticated server-side requests, or an existing backend. Browser-only forms cannot safely hold a secret. You also need authorized accounts, a separate test spreadsheet, and an owner for unresolved submissions.
The example deliberately keeps names, phone numbers, and free-text messages out of the spreadsheet. Staff use contact_ref to locate the original submission in the form system. Check that authorized staff can actually retrieve that reference before building the automation.
1 Define a small payload
The sender supplies one JSON object per submission. These five required text fields are a complete synthetic example:
- submission_id: DEMO-20261004-001
- submitted_at: 2026-10-04T09:00:00Z
- source: website_contact
- service: automation_consulting
- contact_ref: CONTACT-DEMO-001
Generate submission_id once at the source and reuse it for delivery retries. An email address is a poor event key because one person may make several legitimate inquiries. Keep submitted_at as the original UTC timestamp. Allow only website_contact for source and automation_consulting or website_build for service. Restrict identifiers to Latin letters, digits, hyphens, and underscores, with an 80-character maximum.
Validate missing, empty, unexpected, and malformed fields at the sender. The server sets the event identifier and source rather than trusting browser-supplied values as proof of origin. Exclude attachments from this exercise.
2 Prepare the spreadsheet and access
Create a test spreadsheet called Leads Demo with an Intake tab. Put these headers in row one: submission_id, submitted_at, source, service, contact_ref, status. Keep them in columns A through F, without merged cells.
Use an authorized business Google connection. Selecting one spreadsheet in a module does not necessarily limit the connection to that file. Review the consent screen and organizational policy. Make’s custom OAuth setup documents Sheets and Drive scopes. A dedicated account with limited document access helps reduce exposure. Share the spreadsheet only with people who handle these inquiries; this example needs neither email access nor financial information.
3 Configure authenticated intake
Add Webhooks and select Custom webhook. Use Add to name a test endpoint. Configure API Key authentication. Keep its address and key in protected server configuration; the sender supplies the key in the x-make-apikey header and uses Content-Type application/json.
Never put either secret in screenshots, published instructions, HTML, or browser JavaScript. If a provider cannot send an authentication header securely, add a suitable backend rather than removing authentication.
Choose an explicit Data structure under Advanced settings, with the five required text fields. Detecting fields from a sample helps mapping but does not establish request validation. Enforce the allowed business values separately. Do not repeat payload fields in URL query parameters.
4 Reserve the submission before writing
Before creating the store, check the account’s unused allocation. According to Make’s documentation, Free includes 1 MB of Data store storage, and each store requires at least 1 MB. That allocation supports one store, not several separate stores for these exercises at the same time. If it is already allocated, stop before creating another store; do not delete existing data or activate a paid upgrade just to follow the exercise.
Create a test Data store called Intake Demo with a text field named state. Its key is submission_id. Add Check the existence of a record, map that key, and connect a Router with separate conditions for its true and false results.
For a missing key, use Add/replace a record with Key mapped to submission_id, state set to pending, and Overwrite an existing record disabled. Continue only after that reservation succeeds. For an existing key, add no spreadsheet row; return record_exists for operational review.
Enable Process data in order and assign a single writer to this process. Also enable Commit after each module so that the pending reservation survives a later failure. This is a deliberate design choice for this workflow, requiring someone to resolve pending records.
5 Map the tracking row
Add Google Sheets and select Add a Row. Choose the prepared spreadsheet and tab. Map columns A through E to the corresponding five webhook fields, then set column F, status, to the literal text new. Do not map authentication headers, the endpoint address, or the entire incoming bundle.
After the row write, use Update a record with the same key and set state to stored. The synthetic expected result is one row beginning DEMO-20261004-001 and ending new, accompanied by a stored register entry. It is an expected test outcome, not observed Make output.
6 Define failure and response behavior
For this exercise, leave Store incomplete executions off and attach an error route ending in Commit to both the spreadsheet write and register-update modules. This stops the run while retaining supported transactional changes, including the pending reservation. Do not add automatic write retries. Establish an operational error check; silence or a pending response cannot count as success.
Put Webhook response last on the successful route. A proposed response is 201 with status: stored and the submission ID. The existing-key route returns 202 with status: record_exists, which does not prove a completed spreadsheet write. The provider must retain submissions until their business outcome is reconciled.
The standard Accepted response only acknowledges intake. If the provider’s waiting window is too short, use asynchronous delivery with status tracking at the backend. Design around the provider’s actual timeout rather than promising that the browser can wait for the whole workflow.
7 Test duplicates and recovery
Send the sample twice in a test environment: expect one row. Send a new ID: expect another row. Missing fields, an unknown service, and an invalid source should be rejected before writing. Also test closely spaced deliveries and an unavailable Google destination.
For a pending record, search the spreadsheet for submission_id and check that no other attempt is running. One matching row means completing only the register state. If the row is conclusively absent, document the decision, clear that attempt’s reservation, and resend the original event. Two rows or an uncertain outcome require investigation before further writes.
Maintenance and conclusion
Watch queue depth, Data store capacity, credits, and Google request limits. For 429 responses, reduce load and use controlled sender-side backoff. Keep payloads and logs minimal; reference IDs may still be personal information. Success means discoverable submissions, correct rows, and reviewed exceptions. This design reduces duplicate risk and supports recovery, but does not promise exactly-once processing across separate systems.
Download the practice files
This pack contains original, manually authored examples, fictional data and expected test outcomes. It has not been executed in Make, is not an importable blueprint and does not run an automation. Use it with the guide in an authorized test environment only.
Download the Make practice pack (ZIP, 6 KB)
For workflow fit, costs and maintenance, read our Make review for small businesses.
Sources
- Make Help Center: Data stores


