Skip to content

Google Sheets

Add a row to a Google Sheet for every submission, using a Google Cloud service account.

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

  1. In Google Cloud, enable the Google Sheets API for a project.
  2. Create a service account in that project and add a JSON key to it. Google downloads the key as a .json file.
  3. Open the spreadsheet and share it with the service account’s client_email (from the key file) as an editor.
  4. Copy the spreadsheet ID from the sheet’s URL: docs.google.com/spreadsheets/d/<id>/edit.
  5. 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:

  1. Submitted at: {{_submitted_at}}
  2. One column per form field, named after the field’s label (or its name), with its placeholder.
  3. Submission ID: {{_submission_id}}

For a form with name, email and message fields, that’s:

Default mappingjson
{
  "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 Email 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 Authorization header 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.

Type to search…

↑↓ navigate↵ selectEsc close