The Google Sheets destination adds a row to a spreadsheet for every submission. It signs in with a Google Cloud service account, so there is no Google sign-in flow: you give quickforms the service account’s JSON key and share the sheet with it.
Set up
- In Google Cloud, enable the Google Sheets API for a project.
- Create a service account in that project and add a JSON key to it. Google downloads the key as a
.jsonfile. - Open the spreadsheet and share it with the service account’s
client_email(from the key file) as an editor. - Copy the spreadsheet ID from the sheet’s URL:
docs.google.com/spreadsheets/d/<id>/edit. - Add a Google Sheets destination to the form, on its Destinations tab in the dashboard or with the API, and paste in the key and the ID.
Settings
| Setting | Type | Required | Description |
|---|---|---|---|
service_account |
Secret | Yes | The whole JSON key file. It must include client_email and private_key. |
spreadsheet_id |
Text | Yes | The long ID in the sheet’s URL. |
sheet |
Text | No | The sheet (tab) to add rows to. Defaults to Sheet1. |
Like every secret setting, the key is encrypted at rest, never shown again in full, and kept when you update the destination without sending it. See config and secrets.
Columns
The mapping is the row: one entry per column, in order. The keys are names for you to recognise the columns by. Only the values are written, so add a header row to the sheet yourself if you want one.
When you create the destination without a mapping, the columns are:
Submitted at:{{_submitted_at}}- One column per form field, named after the field’s label (or its name), with its placeholder.
Submission ID:{{_submission_id}}
For a form with name, email and message fields, that’s:
{
"Submitted at": "{{_submitted_at}}",
"Name": "{{name}}",
"Email": "{{email}}",
"Message": "{{message}}",
"Submission ID": "{{_submission_id}}"
}Change, reorder or add columns by saving a new mapping. Any placeholder works, and a file field’s placeholder becomes its download link.
What a row looks like
A submission to the form above adds a row like this:
| Submitted at | Name | Message | Submission ID | |
|---|---|---|---|---|
2026-11-06T10:24:00+00:00 |
Ada Lovelace | ada@example.com | Interested in a demo | 01jca9qf3tz5b8m2k7w4r6x0hn |
The values are illustrative.
Values are sent the way a person would type them (USER_ENTERED), so Google Sheets reads numbers and dates as usual. A value starting with =, +, - or @ gets a ' in front, so it is stored as text and not run as a formula.
How it sends
Each submission is one request: a POST to the Sheets API values.append endpoint for the sheet’s A1 range, inserting a new row.
- At send time, quickforms signs a token request with the service account’s private key and swaps it for an access token with the spreadsheets scope. The token is cached for 50 minutes.
- Previews and sandbox submissions never ask Google for a token. They show the request with the
Authorizationheader as[redacted]. - Deliveries are retried and logged like every other destination.
With the API
curl -X POST https://YOUR-QUICKFORMS-HOST/api/v1/forms/FORM_ID/destinations \
-H "Authorization: Bearer $QUICKFORMS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"type": "google_sheets",
"name": "Leads sheet",
"config": {
"service_account": "{\"client_email\": \"quickforms@your-project.iam.gserviceaccount.com\", \"private_key\": \"-----BEGIN PRIVATE KEY-----\\n...\", ...}",
"spreadsheet_id": "SPREADSHEET_ID",
"sheet": "Leads"
}
}'service_account is the key file’s contents as a string. The values are illustrative.
Troubleshooting
| Error in the delivery log | What to check |
|---|---|
| “The service account key must be the JSON file from Google Cloud, with client_email and private_key.” | Paste the whole JSON key file into service_account. |
| “Could not sign with the service account private key.” | The private_key in the key is not a valid private key. |
| “The service account private_key must be the PEM key from the JSON file.” | The private_key is not PEM content. |
| “Google did not return an access token: …” | Google refused the service account. Check the key is current and the account still exists. |
“Google Sheets responded with HTTP 403” or 404 |
Check the sheet is shared with the client_email, the Sheets API is enabled, and the spreadsheet ID and sheet name are right. These are not retried. |