All docs
2 min read

Google Sheets recipe

Two paths: native Sheets integration (one click) or your own Apps Script web app for full control over columns.

Option A — Native Sheets integration

  1. Webhooks → Add integration → Google Sheets.
  2. Authorize. Pick a spreadsheet (or Create new).
  3. We append a row per submission, with a header row matching your form fields. New fields added later get a new column on next submission.

That's it. No code.

Option B — Apps Script web app

Use this when you want custom columns, formulas, conditional formatting, or routing across sheets.

1. Create the Apps Script

In Google Sheets: Extensions → Apps Script. Replace the default file:

const SECRET = PropertiesService.getScriptProperties().getProperty('FORMSPRING_SECRET');

function doPost(e) {
  const raw = e.postData.contents;
  const sig = e.parameter['x-formspring-signature'] || '';

  const expected = Utilities.computeHmacSha256Signature(raw, SECRET)
    .map(b => ('0' + (b & 0xff).toString(16)).slice(-2))
    .join('');

  if (expected !== sig) {
    return ContentService.createTextOutput('unauthorized').setMimeType(ContentService.MimeType.TEXT);
  }

  const body = JSON.parse(raw);
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Submissions')
    || SpreadsheetApp.getActiveSpreadsheet().insertSheet('Submissions');

  const row = [
    body.received_at,
    body.submission_id,
    body.payload.email || '',
    body.payload.name || '',
    body.payload.message || '',
    body.meta?.ip_country || '',
  ];

  sheet.appendRow(row);
  return ContentService.createTextOutput('ok');
}

2. Store the secret

In Apps Script: Project Settings → Script Properties → Add property. Key: FORMSPRING_SECRET. Value: the secret from your webhook.

3. Deploy

Deploy → New deployment → Web app. Execute as: Me. Access: Anyone. Copy the web app URL.

4. Wire it up

In Formspring: Webhooks → Add webhook → Generic. Paste the web app URL.

Apps Script doesn't expose request headers in e.parameter directly — the snippet above expects the signature as a query param. Easier route: configure the webhook URL like https://script.google.com/.../exec?x-formspring-signature=$signature (we don't currently template that). The cleaner approach is a thin proxy (Cloudflare Worker, Lambda) in front that verifies and forwards. Pick your poison.

Notes

  • Sheets caps at ~10M cells per spreadsheet. For high-volume forms, rotate sheets monthly or use a database.
  • Apps Script web apps have a 6-minute execution limit. A simple append is well under it.
  • The native integration handles auth refresh; an Apps Script deployment runs as you and inherits your access.

What's next