{ }
Published on

Trigger a Cloudflare Worker from Google Form Submit (2025 Guide)

Authors
  • avatar
    Name
    Ahmed Farid
    Twitter
    @

TIP

Google Forms still lacks native webhooks, but Apps Script can bridge the gap in 20 lines.

We’ll:

  1. Attach an Apps Script trigger to a Form.
  2. Send JSON payload to a Cloudflare Worker endpoint.
  3. Process the data—e.g., queue in KV, send Telegram, etc.

Total cost: $0.

Table of Contents

1. Prerequisites

  • Google account with Forms & Apps Script access.
  • Cloudflare account with Workers (free plan).

2. Create the Cloudflare Worker Endpoint

2.1 Scaffold

npm create cloudflare@latest form-hook
cd form-hook

Choose TypeScript.

2.2 Worker Code (src/index.ts)

export interface Env {
  STORE: KVNamespace
}

export default {
  async fetch(req: Request, env: Env, ctx: ExecutionContext) {
    if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 })
    const body = await req.json()
    // Example: save latest response to KV
    await env.STORE.put(`resp:${Date.now()}`, JSON.stringify(body))
    return new Response('ok')
  },
}

Add KV in wrangler.toml:

[[kv_namespaces]]
binding = "STORE"
id = "<id>"
preview_id = "<preview_id>"

Deploy:

wrangler deploy --minify

Note the URL https://form-hook.<sub>.workers.dev.

3. Prepare the Google Form

  1. Create Form → Settings → Responses → Enable.
  2. Click Script Editor.

4. Apps Script Webhook

Replace default Code.gs:

const WORKER_URL = 'https://form-hook.<sub>.workers.dev'

function onFormSubmit(e) {
  const responses = e.response.getItemResponses().map((r) => ({
    question: r.getItem().getTitle(),
    answer: r.getResponse(),
  }))

  const payload = JSON.stringify({
    formTitle: FormApp.getActiveForm().getTitle(),
    timestamp: new Date().toISOString(),
    responses,
  })

  UrlFetchApp.fetch(WORKER_URL, {
    method: 'post',
    contentType: 'application/json',
    payload,
    muteHttpExceptions: true,
  })
}

function setupTrigger() {
  ScriptApp.newTrigger('onFormSubmit').forForm(FormApp.getActiveForm()).onFormSubmit().create()
}

Run setupTrigger once → authorize.

5. Test End-to-End

Submit the form → kv:namespace list | head should show new key.

6. Common Extensions

  • Validate reCAPTCHA in Worker.
  • Forward to Zapier or Slack.
  • Store in R2 as CSV.

7. Security & Limits

  • Apps Script daily quota: 20k requests (free).
  • Add secret token header and verify in Worker.
if (req.headers.get('x-hook') !== env.SECRET) return new Response('unauth', { status: 403 })

Store SECRET with wrangler secret and set in UrlFetchApp headers.

8. Conclusion

With a tiny Apps Script and Cloudflare Worker, you’ve built a serverless pipeline reacting to Google Form submissions—scalable, free, and maintainable. 🎉