Overview

Purpose: Create all MHP DE Workshop reflection Google Forms from

google-forms-manifest.yaml using the official

Google Forms API.

Related:

What you get after setup

OutputLocation
10 Google Forms (Mod 0–7 + optional 8–9)Your Google Drive
Form URLs (edit + responder)workshop-2026-v2/trainer/google-forms/forms-output.json
OAuth token (reused on later runs)workshop-2026-v2/trainer/google-forms/token.json *(gitignored)*

You still link each form to Google Sheets manually (one click per form in the Form UI) unless you add a follow-up Drive API script.

Prerequisites

RequirementNotes
Google accountGmail or Google Workspace
Node.js 18+node --version
npmBundled with Node
BrowserFor one-time OAuth consent
~20 minutesFirst-time GCP + OAuth setup

> Cost: Creating a GCP project, enabling the Forms API, and running this script for a one-time workshop rollout is free under standard Forms API quotas. You do not need a billing account for typical use. Google may require billing only if you exceed elevated quotas later in 2026.

Step 1 — Create a Google Cloud project

  1. Open Google Cloud Console.
  2. Click the project picker → New Project.
  3. Name: mhp-de-workshop-forms (any name is fine).
  4. Click Create and select the new project.

No billing account is required for this workflow.

Step 2 — Enable APIs (Forms + Drive)

Enable both in the same GCP project as your OAuth client (credentials.json).

Google Forms API (required)

  1. APIs & ServicesLibrary.
  2. Search Google Forms APIEnable.

Google Drive API (required for --replace delete + banner)

  1. APIs & ServicesLibrary.
  2. Search Google Drive APIEnable.

Direct link (replace project ID if needed):

https://console.cloud.google.com/apis/library/drive.googleapis.com

Or use the link from the error message, e.g.

https://console.developers.google.com/apis/api/drive.googleapis.com/overview?project=YOUR_PROJECT_ID

After enabling, wait 2–5 minutes, then re-run the script. No new credentials.json needed.

Step 4 — Create OAuth client credentials

Where to find it in Google Cloud Console (2025+ UI)

Google moved OAuth settings. Use one of these paths:

PathSteps
A — Direct linkOpen Google Auth Platform → Clients (pick your project at the top if prompted)
B — Left menu☰ Menu → Google Auth platformClients
C — Legacy menu☰ Menu → APIs & ServicesCredentials → under OAuth 2.0 Client IDs, click your client name

If you do not see Google Auth platform, use path C or the direct link in A.

Create the client

  1. On the Clients page, click + Create client (or Create CredentialsOAuth client ID on the legacy Credentials page).
  2. Application type: Desktop app (not “Web application”).
  3. Name: MHP Forms CLI.
  4. Click CreateDownload JSON (download immediately—secrets may not be shown again).

Redirect URIs — Desktop vs Web (read this)

Client type“Authorized redirect URIs” in console?What you do
Desktop appUsually not shown — Google’s docs say the console does not require redirect URIs for desktop appsNothing to add. Loopback (http://127.0.0.1:PORT) is allowed automatically. Our script listens on port 53187.
Web applicationYes — you will see Authorized redirect URIsAdd http://127.0.0.1:53187/oauth2callback or recreate the client as Desktop app (recommended).

If you cannot find Authorized redirect URIs, your client is probably Desktop—that is normal. Skip redirect URI setup and go to Step 6.

If the client type column says Web application, either add the redirect URI on that client’s edit page or create a new Desktop app client and download a new credentials.json.

  1. Save the JSON file as:
textworkshop-2026-v2/trainer/google-forms/credentials.json
Warning

Never commit credentials.json or token.json to git. They are listed in .gitignore.

Step 5 — Install script dependencies

From the repository root:

powershellcd /workshop-2026-v2/trainer/google-forms
npm install

This installs googleapis and js-yaml.

Step 6 — First run (OAuth + create forms)

powershellnode create-forms.mjs

First run only:

  1. The script prints a URL and tries to open your browser (keep the terminal running).
  2. Sign in with the same Gmail address you added under Test users (e.g. juewei.jin@gmail.com).
  3. Click Allow (scope: edit Forms). If you see Access blocked / Error 403: access_denied, see Fix: Access blocked (403) below.
  4. After Allow, Google redirects to 127.0.0.1:53187 — you should see Authorization successful in the browser (not *connection refused*).
  5. Return to the terminal; the script saves token.json and creates all forms.

If the browser shows This site can’t be reached / localhost refused to connect, see Fix: localhost refused to connect below.

Later runs reuse token.json — no browser step unless the token expired.

Dry run (no API calls)

powershellnode create-forms.mjs --dry-run

Prints how many questions would be created per form.

Single module only

powershellnode create-forms.mjs --module 4
node create-forms.mjs --module final

Replace previous forms (recommended after manifest changes)

powershellnode create-forms.mjs --replace

Deletes every form listed in forms-output.json, then recreates from the manifest (quiz keys, banner, Drive folder).

Move existing forms into one Drive folder

powershellnode create-forms.mjs --organize-folder

Uses drive_folder from google-forms-manifest.yaml. Does not create new forms.

Regenerate trainer answer key (Markdown)

powershellnode sync-grading-artifacts.mjs

Reads google-forms-grading.yaml → updates google-forms-reflection-answer-key.md.

Step 7 — Review output

Open forms-output.json (includes driveFolder.folderUrl — all forms are moved into that Drive folder):

json{
  "generatedAt": "2026-05-31T12:00:00.000Z",
  "forms": [
    {
      "module": 0,
      "title": "MHP DE Workshop — Mod 0 Reflection",
      "formId": "1FAIpQLS...",
      "editUrl": "https://docs.google.com/forms/d/1FAIpQLS.../edit",
      "responderUrl": "https://docs.google.com/forms/d/1FAIpQLS.../viewform"
    }
  ]
}
URLUse in class
editUrlTrainer edits questions or settings
responderUrlShort link / QR for trainees

Share responder URLs after each module reflection (see module-delivery-pattern.md).

Step 9 — Publish forms for trainees

In each form Settings:

  • Turn off Restrict to users in your organization if trainees use personal Gmail.
  • Choose whether to collect email addresses (or rely on the "Full name" question in the manifest).
  • Confirm Accepting responses is on.

Generate QR codes from each responderUrl (any QR generator).

Updating forms after manifest changes

  1. Edit google-forms-manifest.yaml.
  2. Option A (recommended) — Replace via script (uses forms-output.json from your last successful run):
powershell   node create-forms.mjs --replace --dry-run   # preview deletes
   node create-forms.mjs --replace             # delete + recreate all
   node create-forms.mjs --module 4 --replace  # one module only

Deletes go to Google Drive trash (Forms file). Linked Sheets keep historical responses; point Sheets at the new forms if needed.

  1. Option Bnode create-forms.mjs --delete-only then create without --replace.
  2. Option C — Manually edit existing forms in the Google Forms UI.

The script does not update existing forms in place; without --replace it adds new forms each run.

Quiz mode and auto-grading (Mod 0–9)

Reflection forms are created as quizzes using google-forms-grading.yaml:

  • Correct answers and point values (radio = 1 pt, checkbox = 2 pt by default)
  • Correct/incorrect feedback (when_right / when_wrong) shown after submit
  • Scales, grids, Module 0.7 risk question, and final survey are not auto-graded

Regenerate trainer answer key after editing grading:

powershellnode sync-grading-artifacts.mjs

In Google Forms → Settings → Quizzes, set Release grade (e.g. immediately after submission).

Fix: localhost refused to connect

After you click Continue, the browser may show:

> *This site can’t be reached* — *localhost refused to connect*

That means Google redirected to localhost but the script was not running a local server yet (or the terminal was closed).

Fix (Desktop client — most users)

  1. Confirm OAuth client type is Desktop app on Clients — you do not need to add redirect URIs.
  2. Delete old token: remove trainer/google-forms/token.json.
  3. Run the script and keep the terminal open:
powershellcd /workshop-2026-v2/trainer/google-forms
node create-forms.mjs
  1. Wait until the terminal prints: OAuth: waiting for browser callback on 127.0.0.1:53187 ...
  2. Complete sign-in in the browser → you should see Authorization successful (not connection refused).

Fix (only if your client type is Web application)

  1. Clients → click the Web client → Authorized redirect URIs → add:
text   http://127.0.0.1:53187/oauth2callback
  1. Save, delete token.json, run node create-forms.mjs again.

Still failing?

CauseAction
Terminal closed before AllowScript must stay running until browser shows success
Wrong client typeUse Desktop app + new credentials.json
redirect_uri_mismatchWeb client: add URI above; Desktop: use Desktop credentials only
Firewall / VPNAllow local 127.0.0.1

Fix: Access blocked (403 access_denied)

If the browser shows:

> *Access blocked: … has not completed the Google verification process*

> *Error 403: access_denied*

the OAuth app is in Testing mode and your Google account is not on the Test users list (or you signed in with a different account).

Fix in Google Cloud Console (2 minutes)

  1. Open Google Cloud Console → select project mhp-de-workshop-forms (or your project name).
  2. APIs & ServicesOAuth consent screen.
  3. Scroll to Test users+ ADD USERS.
  4. Enter the exact email you use in the browser, e.g. juewei.jin@gmail.comSave.
  5. Wait 1–2 minutes, then run node create-forms.mjs again and open a new OAuth URL (or incognito window).
  6. Sign in only as that test user—not a work account alias unless that alias is also added.

Checklist

CheckAction
Correct GCP projectOAuth client and consent screen must be in the same project as credentials.json
External + TestingNormal for Gmail; you must use Test users, not “Publish app”
Email matchesLogin email = one of the Test users (character-for-character)
Stale browser sessionTry incognito or sign out of other Google accounts first
Workspace orgIf your company blocks unverified apps, use Internal user type on Workspace, or create forms manually

If you have Google Workspace (company account)

On OAuth consent screen, if Internal is available:

  1. Switch user type to Internal (org-only; no Test users list).
  2. Only accounts in your organization can authorize—use your @company.com account consistently.

What not to do

  • Publish app to production without verification—you will hit Google’s review process; unnecessary for a trainer script.
  • Add scopes beyond forms.body unless you need them—extra scopes slow consent and verification.

Troubleshooting

SymptomFix
credentials.json not foundDownload OAuth Desktop JSON to trainer/google-forms/credentials.json
localhost refused to connectFix section above—run script first; redirect URI only for Web clients
Cannot find redirect URIsNormal for Desktop app—use Clients and verify type is Desktop
Access blocked / 403 access_deniedFix section above—add juewei.jin@gmail.com (or your login) under Test users
access_denied on consent screenSame as above; email must be on Test users for External apps
Google Forms API has not been used...Enable Google Forms API in the correct GCP project
Google Drive API has not been used... / it is disabledEnable Google Drive API in the same project → wait a few minutes → re-run (no new credentials)
invalid_grant / token expiredDelete token.json and run node create-forms.mjs again
Insufficient Permission (delete or banner)Add scope drive (not only drive.file) on consent screen → delete token.json → run node create-forms.mjs again and complete OAuth
Failed to fetch image from source_uriDelete banner-drive-cache.json, re-run (script picks a Forms-compatible Drive URL). Or set banner_source_uri in manifest to any public image URL. Or add banner manually in Form editor
Banner missing on formsSame as above; or add image manually in each Form editor
--replace delete failedOld forms still in Drive — trash them at Google Drive (filter: Google Forms); new URLs are in forms-output.json
Enterprise blocks third-party appsAsk IT to allow the OAuth client or use manual Form creation
Grid questions missing optionsCheck manifest GRID_RADIO has rows and columns arrays

Security checklist

  • credentials.json and token.json are not committed
  • forms-output.json contains only public form IDs (safe to share with co-trainers)
  • Workshop forms owned by a team Google account if possible
  • Revoke access later: Google Account → Third-party access

File layout

textworkshop-2026-v2/trainer/google-forms/
├── google-forms-manifest.yaml          ← source of truth for questions
├── google-forms-grading.yaml           ← quiz keys + feedback (Mod 0–9)
├── google-forms-reflection-design.md
├── google-forms-reflection-design.html
├── google-forms-reflection-answer-key.md
├── google-forms-gcp-setup-guide.md     ← this document
├── google-forms-gcp-setup-guide.html
├── create-forms.mjs                    ← automation entry point
├── sync-grading-artifacts.mjs          ← answer-key.md from grading YAML
├── package.json
├── credentials.json                    ← you download (gitignored)
├── credentials.json.example
├── token.json                          ← created on first auth (gitignored)
└── forms-output.json                   ← URLs after successful run

Quick reference commands

powershellcd /workshop-2026-v2/trainer/google-forms
npm install
node create-forms.mjs --dry-run
node create-forms.mjs
node create-forms.mjs --replace
node create-forms.mjs --module 7 --replace
node create-forms.mjs --delete-only
node create-forms.mjs --organize-folder
node sync-grading-artifacts.mjs

Regenerate HTML companion for this guide:

powershellpython d:\GitRepo\MHPDataEngineerWorkshop\workshop-2026-v2\trainer\build_trainer_html.py google-forms/google-forms-gcp-setup-guide.md

Document history

DateChange
2026-05-31Initial GCP + Forms API automation setup guide
2026-05-31HTML companion: google-forms-gcp-setup-guide.html
2026-05-31Consolidated under trainer/google-forms/
2026-05-31Added 403 access_denied / Test users fix section
2026-05-31Local OAuth callback on 127.0.0.1:53187; localhost troubleshooting
2026-05-31Console navigation: Google Auth Platform → Clients; Desktop has no redirect URI field