[{"data":1,"prerenderedAt":833},["ShallowReactive",2],{"content-query-myKpOMgfVr":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"layout":10,"author":11,"tags":12,"categories":17,"date":19,"image":20,"faq":21,"body":31,"_type":826,"_id":827,"_source":828,"_file":829,"_stem":830,"_extension":831,"sitemap":832},"/blog/google-forms-webhook","blog",false,"","How to Send a Webhook from Google Forms (Apps Script)","Google Forms has no built-in webhooks, but a few lines of Apps Script and an on-form-submit trigger will POST every response to your URL. Copy-paste code.","post","Karolis Rusenas",[13,14,15,16],"google forms","webhooks","apps script","guide",[18],"guides","2026-07-21 10:30:00","/images/generic/webhooks.png",[22,25,28],{"q":23,"a":24},"Does Google Forms support webhooks?","Not natively. Google Forms has no built-in webhook option, but you can add one with Google Apps Script: attach a script to the form, POST the response to your endpoint with UrlFetchApp, and run it from an installable 'On form submit' trigger.",{"q":26,"a":27},"Do I need to deploy the Apps Script as a web app?","No. For sending a webhook when a form is submitted you only need an installable on-form-submit trigger — not a web-app deployment. Deploying as a web app is for a different use case (receiving inbound HTTP requests), and adding it here just creates a public endpoint you don't need.",{"q":29,"a":30},"Why does my Google Forms webhook fail with a permissions error?","A simple trigger (a function literally named onFormSubmit that runs automatically) can't call services that need authorization, like UrlFetchApp. Use an installable trigger instead: Triggers → Add Trigger → choose your function, event source 'From form', event type 'On form submit', then authorize it once.",{"type":32,"children":33,"toc":818},"root",[34,66,73,78,109,129,135,154,160,173,557,575,581,607,677,686,692,719,725,746,791,812],{"type":35,"tag":36,"props":37,"children":38},"element","p",{},[39,42,48,50,55,57,64],{"type":40,"value":41},"text","Google Forms is great at collecting responses but has ",{"type":35,"tag":43,"props":44,"children":45},"strong",{},[46],{"type":40,"value":47},"no native webhooks",{"type":40,"value":49}," — there's no field where you paste a URL. The fix is a few lines of ",{"type":35,"tag":43,"props":51,"children":52},{},[53],{"type":40,"value":54},"Google Apps Script",{"type":40,"value":56}," that POST each response to your endpoint. This guide shows the correct, minimal setup, and avoids a step you'll see in other tutorials that you don't actually need. (New to webhooks? See ",{"type":35,"tag":58,"props":59,"children":61},"a",{"href":60},"/blog/what-is-webhook",[62],{"type":40,"value":63},"what is a webhook",{"type":40,"value":65},".)",{"type":35,"tag":67,"props":68,"children":70},"h2",{"id":69},"the-approach",[71],{"type":40,"value":72},"The approach",{"type":35,"tag":36,"props":74,"children":75},{},[76],{"type":40,"value":77},"Every Google Form can have a bound Apps Script. We'll:",{"type":35,"tag":79,"props":80,"children":81},"ol",{},[82,97],{"type":35,"tag":83,"props":84,"children":85},"li",{},[86,88,95],{"type":40,"value":87},"Add a function that turns a submitted response into JSON and POSTs it with ",{"type":35,"tag":89,"props":90,"children":92},"code",{"className":91},[],[93],{"type":40,"value":94},"UrlFetchApp",{"type":40,"value":96},".",{"type":35,"tag":83,"props":98,"children":99},{},[100,102,107],{"type":40,"value":101},"Wire it to an ",{"type":35,"tag":43,"props":103,"children":104},{},[105],{"type":40,"value":106},"installable \"On form submit\" trigger",{"type":40,"value":108}," so it runs on every submission.",{"type":35,"tag":36,"props":110,"children":111},{},[112,114,119,121,127],{"type":40,"value":113},"That's it — ",{"type":35,"tag":43,"props":115,"children":116},{},[117],{"type":40,"value":118},"no \"Deploy as web app\"",{"type":40,"value":120}," step. (Deploying as a web app is for ",{"type":35,"tag":122,"props":123,"children":124},"em",{},[125],{"type":40,"value":126},"receiving",{"type":40,"value":128}," HTTP requests, not for sending one on submit; adding it here just exposes an endpoint you don't need.)",{"type":35,"tag":67,"props":130,"children":132},{"id":131},"step-1-open-the-script-editor",[133],{"type":40,"value":134},"Step 1 — open the script editor",{"type":35,"tag":36,"props":136,"children":137},{},[138,140,145,147,152],{"type":40,"value":139},"In the form editor, click the ",{"type":35,"tag":43,"props":141,"children":142},{},[143],{"type":40,"value":144},"⋮ (three dots)",{"type":40,"value":146}," menu (top-right, near Send) → ",{"type":35,"tag":43,"props":148,"children":149},{},[150],{"type":40,"value":151},"Apps Script",{"type":40,"value":153},". This opens a script bound to your form.",{"type":35,"tag":67,"props":155,"children":157},{"id":156},"step-2-add-the-code",[158],{"type":40,"value":159},"Step 2 — add the code",{"type":35,"tag":36,"props":161,"children":162},{},[163,165,171],{"type":40,"value":164},"Replace the default contents with this, and set ",{"type":35,"tag":89,"props":166,"children":168},{"className":167},[],[169],{"type":40,"value":170},"ENDPOINT_URL",{"type":40,"value":172}," to your webhook URL:",{"type":35,"tag":174,"props":175,"children":179},"pre",{"className":176,"code":177,"language":178,"meta":7,"style":7},"language-javascript shiki shiki-themes github-dark","const ENDPOINT_URL = 'https://your-endpoint.example.com/webhook';\n\nfunction onFormSubmit(e) {\n  const payload = {};\n  // e.response is the FormResponse for THIS submission\n  e.response.getItemResponses().forEach(function (item) {\n    payload[item.getItem().getTitle()] = item.getResponse();\n  });\n  payload.submittedAt = e.response.getTimestamp();\n\n  UrlFetchApp.fetch(ENDPOINT_URL, {\n    method: 'post',\n    contentType: 'application/json',\n    payload: JSON.stringify(payload),\n    muteHttpExceptions: true,\n  });\n}\n","javascript",[180],{"type":35,"tag":89,"props":181,"children":182},{"__ignoreMap":7},[183,218,228,259,282,292,338,386,395,422,430,457,476,494,522,540,548],{"type":35,"tag":184,"props":185,"children":188},"span",{"class":186,"line":187},"line",1,[189,195,201,206,212],{"type":35,"tag":184,"props":190,"children":192},{"style":191},"--shiki-default:#F97583",[193],{"type":40,"value":194},"const",{"type":35,"tag":184,"props":196,"children":198},{"style":197},"--shiki-default:#79B8FF",[199],{"type":40,"value":200}," ENDPOINT_URL",{"type":35,"tag":184,"props":202,"children":203},{"style":191},[204],{"type":40,"value":205}," =",{"type":35,"tag":184,"props":207,"children":209},{"style":208},"--shiki-default:#9ECBFF",[210],{"type":40,"value":211}," 'https://your-endpoint.example.com/webhook'",{"type":35,"tag":184,"props":213,"children":215},{"style":214},"--shiki-default:#E1E4E8",[216],{"type":40,"value":217},";\n",{"type":35,"tag":184,"props":219,"children":221},{"class":186,"line":220},2,[222],{"type":35,"tag":184,"props":223,"children":225},{"emptyLinePlaceholder":224},true,[226],{"type":40,"value":227},"\n",{"type":35,"tag":184,"props":229,"children":231},{"class":186,"line":230},3,[232,237,243,248,254],{"type":35,"tag":184,"props":233,"children":234},{"style":191},[235],{"type":40,"value":236},"function",{"type":35,"tag":184,"props":238,"children":240},{"style":239},"--shiki-default:#B392F0",[241],{"type":40,"value":242}," onFormSubmit",{"type":35,"tag":184,"props":244,"children":245},{"style":214},[246],{"type":40,"value":247},"(",{"type":35,"tag":184,"props":249,"children":251},{"style":250},"--shiki-default:#FFAB70",[252],{"type":40,"value":253},"e",{"type":35,"tag":184,"props":255,"children":256},{"style":214},[257],{"type":40,"value":258},") {\n",{"type":35,"tag":184,"props":260,"children":262},{"class":186,"line":261},4,[263,268,273,277],{"type":35,"tag":184,"props":264,"children":265},{"style":191},[266],{"type":40,"value":267},"  const",{"type":35,"tag":184,"props":269,"children":270},{"style":197},[271],{"type":40,"value":272}," payload",{"type":35,"tag":184,"props":274,"children":275},{"style":191},[276],{"type":40,"value":205},{"type":35,"tag":184,"props":278,"children":279},{"style":214},[280],{"type":40,"value":281}," {};\n",{"type":35,"tag":184,"props":283,"children":285},{"class":186,"line":284},5,[286],{"type":35,"tag":184,"props":287,"children":289},{"style":288},"--shiki-default:#6A737D",[290],{"type":40,"value":291},"  // e.response is the FormResponse for THIS submission\n",{"type":35,"tag":184,"props":293,"children":295},{"class":186,"line":294},6,[296,301,306,311,316,320,324,329,334],{"type":35,"tag":184,"props":297,"children":298},{"style":214},[299],{"type":40,"value":300},"  e.response.",{"type":35,"tag":184,"props":302,"children":303},{"style":239},[304],{"type":40,"value":305},"getItemResponses",{"type":35,"tag":184,"props":307,"children":308},{"style":214},[309],{"type":40,"value":310},"().",{"type":35,"tag":184,"props":312,"children":313},{"style":239},[314],{"type":40,"value":315},"forEach",{"type":35,"tag":184,"props":317,"children":318},{"style":214},[319],{"type":40,"value":247},{"type":35,"tag":184,"props":321,"children":322},{"style":191},[323],{"type":40,"value":236},{"type":35,"tag":184,"props":325,"children":326},{"style":214},[327],{"type":40,"value":328}," (",{"type":35,"tag":184,"props":330,"children":331},{"style":250},[332],{"type":40,"value":333},"item",{"type":35,"tag":184,"props":335,"children":336},{"style":214},[337],{"type":40,"value":258},{"type":35,"tag":184,"props":339,"children":341},{"class":186,"line":340},7,[342,347,352,356,361,366,371,376,381],{"type":35,"tag":184,"props":343,"children":344},{"style":214},[345],{"type":40,"value":346},"    payload[item.",{"type":35,"tag":184,"props":348,"children":349},{"style":239},[350],{"type":40,"value":351},"getItem",{"type":35,"tag":184,"props":353,"children":354},{"style":214},[355],{"type":40,"value":310},{"type":35,"tag":184,"props":357,"children":358},{"style":239},[359],{"type":40,"value":360},"getTitle",{"type":35,"tag":184,"props":362,"children":363},{"style":214},[364],{"type":40,"value":365},"()] ",{"type":35,"tag":184,"props":367,"children":368},{"style":191},[369],{"type":40,"value":370},"=",{"type":35,"tag":184,"props":372,"children":373},{"style":214},[374],{"type":40,"value":375}," item.",{"type":35,"tag":184,"props":377,"children":378},{"style":239},[379],{"type":40,"value":380},"getResponse",{"type":35,"tag":184,"props":382,"children":383},{"style":214},[384],{"type":40,"value":385},"();\n",{"type":35,"tag":184,"props":387,"children":389},{"class":186,"line":388},8,[390],{"type":35,"tag":184,"props":391,"children":392},{"style":214},[393],{"type":40,"value":394},"  });\n",{"type":35,"tag":184,"props":396,"children":398},{"class":186,"line":397},9,[399,404,408,413,418],{"type":35,"tag":184,"props":400,"children":401},{"style":214},[402],{"type":40,"value":403},"  payload.submittedAt ",{"type":35,"tag":184,"props":405,"children":406},{"style":191},[407],{"type":40,"value":370},{"type":35,"tag":184,"props":409,"children":410},{"style":214},[411],{"type":40,"value":412}," e.response.",{"type":35,"tag":184,"props":414,"children":415},{"style":239},[416],{"type":40,"value":417},"getTimestamp",{"type":35,"tag":184,"props":419,"children":420},{"style":214},[421],{"type":40,"value":385},{"type":35,"tag":184,"props":423,"children":425},{"class":186,"line":424},10,[426],{"type":35,"tag":184,"props":427,"children":428},{"emptyLinePlaceholder":224},[429],{"type":40,"value":227},{"type":35,"tag":184,"props":431,"children":433},{"class":186,"line":432},11,[434,439,444,448,452],{"type":35,"tag":184,"props":435,"children":436},{"style":214},[437],{"type":40,"value":438},"  UrlFetchApp.",{"type":35,"tag":184,"props":440,"children":441},{"style":239},[442],{"type":40,"value":443},"fetch",{"type":35,"tag":184,"props":445,"children":446},{"style":214},[447],{"type":40,"value":247},{"type":35,"tag":184,"props":449,"children":450},{"style":197},[451],{"type":40,"value":170},{"type":35,"tag":184,"props":453,"children":454},{"style":214},[455],{"type":40,"value":456},", {\n",{"type":35,"tag":184,"props":458,"children":460},{"class":186,"line":459},12,[461,466,471],{"type":35,"tag":184,"props":462,"children":463},{"style":214},[464],{"type":40,"value":465},"    method: ",{"type":35,"tag":184,"props":467,"children":468},{"style":208},[469],{"type":40,"value":470},"'post'",{"type":35,"tag":184,"props":472,"children":473},{"style":214},[474],{"type":40,"value":475},",\n",{"type":35,"tag":184,"props":477,"children":479},{"class":186,"line":478},13,[480,485,490],{"type":35,"tag":184,"props":481,"children":482},{"style":214},[483],{"type":40,"value":484},"    contentType: ",{"type":35,"tag":184,"props":486,"children":487},{"style":208},[488],{"type":40,"value":489},"'application/json'",{"type":35,"tag":184,"props":491,"children":492},{"style":214},[493],{"type":40,"value":475},{"type":35,"tag":184,"props":495,"children":497},{"class":186,"line":496},14,[498,503,508,512,517],{"type":35,"tag":184,"props":499,"children":500},{"style":214},[501],{"type":40,"value":502},"    payload: ",{"type":35,"tag":184,"props":504,"children":505},{"style":197},[506],{"type":40,"value":507},"JSON",{"type":35,"tag":184,"props":509,"children":510},{"style":214},[511],{"type":40,"value":96},{"type":35,"tag":184,"props":513,"children":514},{"style":239},[515],{"type":40,"value":516},"stringify",{"type":35,"tag":184,"props":518,"children":519},{"style":214},[520],{"type":40,"value":521},"(payload),\n",{"type":35,"tag":184,"props":523,"children":525},{"class":186,"line":524},15,[526,531,536],{"type":35,"tag":184,"props":527,"children":528},{"style":214},[529],{"type":40,"value":530},"    muteHttpExceptions: ",{"type":35,"tag":184,"props":532,"children":533},{"style":197},[534],{"type":40,"value":535},"true",{"type":35,"tag":184,"props":537,"children":538},{"style":214},[539],{"type":40,"value":475},{"type":35,"tag":184,"props":541,"children":543},{"class":186,"line":542},16,[544],{"type":35,"tag":184,"props":545,"children":546},{"style":214},[547],{"type":40,"value":394},{"type":35,"tag":184,"props":549,"children":551},{"class":186,"line":550},17,[552],{"type":35,"tag":184,"props":553,"children":554},{"style":214},[555],{"type":40,"value":556},"}\n",{"type":35,"tag":36,"props":558,"children":559},{},[560,562,573],{"type":40,"value":561},"Using the ",{"type":35,"tag":43,"props":563,"children":564},{},[565,567],{"type":40,"value":566},"event object ",{"type":35,"tag":89,"props":568,"children":570},{"className":569},[],[571],{"type":40,"value":572},"e.response",{"type":40,"value":574}," is more reliable than fetching \"the latest response\" from the form — it's exactly the submission that fired the trigger, with no race conditions.",{"type":35,"tag":67,"props":576,"children":578},{"id":577},"step-3-add-an-installable-trigger",[579],{"type":40,"value":580},"Step 3 — add an installable trigger",{"type":35,"tag":36,"props":582,"children":583},{},[584,586,591,593,598,600,605],{"type":40,"value":585},"This is the step that trips people up. Because ",{"type":35,"tag":89,"props":587,"children":589},{"className":588},[],[590],{"type":40,"value":94},{"type":40,"value":592}," needs authorization, a ",{"type":35,"tag":122,"props":594,"children":595},{},[596],{"type":40,"value":597},"simple",{"type":40,"value":599}," trigger won't work — you need an ",{"type":35,"tag":43,"props":601,"children":602},{},[603],{"type":40,"value":604},"installable",{"type":40,"value":606}," one:",{"type":35,"tag":79,"props":608,"children":609},{},[610,622,631,660],{"type":35,"tag":83,"props":611,"children":612},{},[613,615,620],{"type":40,"value":614},"In the Apps Script editor, click the ",{"type":35,"tag":43,"props":616,"children":617},{},[618],{"type":40,"value":619},"Triggers",{"type":40,"value":621}," icon (the alarm clock) in the left sidebar.",{"type":35,"tag":83,"props":623,"children":624},{},[625,630],{"type":35,"tag":43,"props":626,"children":627},{},[628],{"type":40,"value":629},"Add Trigger",{"type":40,"value":96},{"type":35,"tag":83,"props":632,"children":633},{},[634,636,645,647,652,654,659],{"type":40,"value":635},"Choose function ",{"type":35,"tag":43,"props":637,"children":638},{},[639],{"type":35,"tag":89,"props":640,"children":642},{"className":641},[],[643],{"type":40,"value":644},"onFormSubmit",{"type":40,"value":646},", event source ",{"type":35,"tag":43,"props":648,"children":649},{},[650],{"type":40,"value":651},"From form",{"type":40,"value":653},", event type ",{"type":35,"tag":43,"props":655,"children":656},{},[657],{"type":40,"value":658},"On form submit",{"type":40,"value":96},{"type":35,"tag":83,"props":661,"children":662},{},[663,668,670,675],{"type":35,"tag":43,"props":664,"children":665},{},[666],{"type":40,"value":667},"Save",{"type":40,"value":669},", then ",{"type":35,"tag":43,"props":671,"children":672},{},[673],{"type":40,"value":674},"authorize",{"type":40,"value":676}," the script when Google prompts (you'll approve the permissions once).",{"type":35,"tag":36,"props":678,"children":679},{},[680],{"type":35,"tag":681,"props":682,"children":685},"img",{"alt":683,"src":684},"The Apps Script Add Trigger dialog set to function onFormSubmit, event source \"From form\" and event type \"On form submit\"","/images/guides/google-forms-apps-script-trigger.png",[],{"type":35,"tag":67,"props":687,"children":689},{"id":688},"step-4-test-it",[690],{"type":40,"value":691},"Step 4 — test it",{"type":35,"tag":36,"props":693,"children":694},{},[695,697,702,704,710,712,717],{"type":40,"value":696},"First, capture what the form sends by pointing ",{"type":35,"tag":89,"props":698,"children":700},{"className":699},[],[701],{"type":40,"value":170},{"type":40,"value":703}," at a free ",{"type":35,"tag":58,"props":705,"children":707},{"href":706},"/webhook-bin",[708],{"type":40,"value":709},"Webhook Bin",{"type":40,"value":711}," — submit a test response and you'll see the exact JSON arrive. Then switch ",{"type":35,"tag":89,"props":713,"children":715},{"className":714},[],[716],{"type":40,"value":170},{"type":40,"value":718}," to your real handler.",{"type":35,"tag":67,"props":720,"children":722},{"id":721},"where-the-webhook-goes-next",[723],{"type":40,"value":724},"Where the webhook goes next",{"type":35,"tag":36,"props":726,"children":727},{},[728,730,736,738,744],{"type":40,"value":729},"Once your form responses are flowing as webhooks, Webhook Relay can route them anywhere: ",{"type":35,"tag":58,"props":731,"children":733},{"href":732},"/features/transform-webhooks",[734],{"type":40,"value":735},"transform",{"type":40,"value":737}," each submission and ",{"type":35,"tag":58,"props":739,"children":741},{"href":740},"/features/webhook-multiple-destinations",[742],{"type":40,"value":743},"fan it out",{"type":40,"value":745}," to multiple destinations, or deliver to a service on your own machine.",{"type":35,"tag":747,"props":748,"children":749},"ul",{},[750,761,780],{"type":35,"tag":83,"props":751,"children":752},{},[753,759],{"type":35,"tag":58,"props":754,"children":756},{"href":755},"/blog/webhook-to-google-sheets",[757],{"type":40,"value":758},"Webhook to Google Sheets",{"type":40,"value":760}," — log every submission to a sheet.",{"type":35,"tag":83,"props":762,"children":763},{},[764,770,772,778],{"type":35,"tag":58,"props":765,"children":767},{"href":766},"/blog/webhook-to-slack",[768],{"type":40,"value":769},"Webhook to Slack",{"type":40,"value":771}," or ",{"type":35,"tag":58,"props":773,"children":775},{"href":774},"/blog/webhook-to-discord",[776],{"type":40,"value":777},"Discord",{"type":40,"value":779}," — get notified on each response.",{"type":35,"tag":83,"props":781,"children":782},{},[783,789],{"type":35,"tag":58,"props":784,"children":786},{"href":785},"/webhooks",[787],{"type":40,"value":788},"Receive it on localhost",{"type":40,"value":790}," — drive a local handler with real submissions.",{"type":35,"tag":36,"props":792,"children":793},{},[794,796,801,803,811],{"type":40,"value":795},"Ready to turn form submissions into webhooks? ",{"type":35,"tag":58,"props":797,"children":798},{"href":706},[799],{"type":40,"value":800},"Grab a free Webhook Bin",{"type":40,"value":802}," to see your first one, or ",{"type":35,"tag":58,"props":804,"children":808},{"href":805,"rel":806},"https://my.webhookrelay.com/register",[807],"nofollow",[809],{"type":40,"value":810},"start forwarding for free",{"type":40,"value":96},{"type":35,"tag":813,"props":814,"children":815},"style",{},[816],{"type":40,"value":817},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":230,"depth":230,"links":819},[820,821,822,823,824,825],{"id":69,"depth":220,"text":72},{"id":131,"depth":220,"text":134},{"id":156,"depth":220,"text":159},{"id":577,"depth":220,"text":580},{"id":688,"depth":220,"text":691},{"id":721,"depth":220,"text":724},"markdown","content:blog:google-forms-webhook.md","content","blog/google-forms-webhook.md","blog/google-forms-webhook","md",{"loc":4},1786379868034]