Google Workspace CLI for beginners: create and verify a feedback form
A feedback form can look finished even when one question is out of order or the response link is more open than intended. This guide therefore builds an unpublished four-question Google Form with Google Workspace CLI and checks the saved version before anyone receives access.
The example is a two-minute feedback form after an AI workshop. You will use Google Workspace CLI, gws, without writing an API client. Allow 10–15 minutes once the tool is installed and authenticated. Initial OAuth setup can take longer.
What you will have at the end
The form contains four questions in a fixed order:
- a required 1–5 scale about how clear the next step was;
- a required single-choice question about what the participant wants to use first;
- an optional long-answer question about what still needs clarification;
- a required yes-or-no question about the next guide.
You will then check the title, item count, order, question types, required fields, email collection, and access. The form remains unpublished when the guide ends.
Before you start
You need a Google account, a Google Cloud project, and OAuth access to the Forms and Drive operations used here. Install the current version from the project's instructions. With Node.js, you can use:
npm install -g @googleworkspace/cli
You can start initial configuration with gws auth setup, then select only the services needed for this workflow:
gws auth login -s forms,drive
gws --version
gws lives in Google's googleworkspace organization on GitHub, but the project explicitly says it is not an officially supported Google product. It is also pre-v1, so check the installed version and command help before copying old commands. This workflow was tested with gws 0.22.5 on August 19, 2026.
Source: Google Workspace CLI on GitHub.
Let the tool's login flow or your operating system keyring handle authentication. Do not paste OAuth tokens or client secrets into request files, chats, or screenshots.
Step 1: write the question specification first
Start with the content rather than the command. A short specification makes the later check easy:
[
\{"index": 0, "type": "scale_1_5", "required": true\},
\{"index": 1, "type": "single_choice", "required": true\},
\{"index": 2, "type": "paragraph", "required": false\},
\{"index": 3, "type": "yes_no", "required": true\}
]
Four mixed question types are enough to test the workflow. You do not need a larger form to learn the method.
Step 2: create an unpublished shell
Save the first request body as form-create.json:
\{
"info": \{
"title": "AI workshop: two-minute feedback"
\}
\}
Create the form with unpublished=true:
gws forms forms create \
--params '\{"unpublished":true\}' \
--json "$(<form-create.json)" \
--format json
Store the returned form ID locally as FORM_ID, but keep it out of public screenshots. The first call creates only the form shell. Questions and the description belong in the next step.
Google documents that API-created forms after June 30, 2026 are unpublished by default. We still pass unpublished=true explicitly because older reference text and dynamic command help are not fully consistent about the default.
Source: Create a form or quiz.
Step 3: add the questions in one ordered batch
Create questions.json with one updateFormInfo request followed by four createItem requests. Give each question location 0, 1, 2, or 3. The optional long-answer question uses required: false; the other three use required: true.
Send the complete body in one call:
gws forms forms batchUpdate \
--params "\{\"formId\":\"$FORM_ID\"\}" \
--json "$(<questions.json)" \
--format json
Order matters. Google Forms API validates the requests one at a time. Index 1 is not valid until index 0 exists. If one dependent request is invalid, the batch does not write the other changes.
Source: Update a form or quiz.
Step 4: read the saved form back
A successful update response does not prove that the intended content was stored. Fetch the form again:
gws forms forms get \
--params "\{\"formId\":\"$FORM_ID\"\}" \
--format json > form-readback.json
Use a short human review instead of skimming the entire JSON response:
- the title matches;
- exactly four questions exist;
- the order is scale, single choice, long answer, yes or no;
- required fields are yes, yes, no, yes;
emailCollectionTypeisDO_NOT_COLLECT.
JSON may omit boolean defaults that are false. A missing required field on the optional question should therefore be interpreted as false under the API model, not as a skipped check.
In Hammer's test, all four questions and required settings matched. The form ID, responder URL, and account identifiers have been removed from the public material.
Step 5: check publication and responder access separately
A form's publication state and its published Drive permissions are separate controls. List permissions in the published view:
gws drive permissions list \
--params "\{\"fileId\":\"$FORM_ID\",\"includePermissionsForView\":\"published\",\"fields\":\"permissions(id,type,role,view)\"\}" \
--format json
Look for a permission with view=published and role=reader. If an open published-reader permission exists, delete it by permission ID and list the permissions again:
gws drive permissions delete \
--params "\{\"fileId\":\"$FORM_ID\",\"permissionId\":\"$PERMISSION_ID\"\}"
Do not publish the form or submit a fake response merely to test it. In the verified run, only the owner permission remained, and an anonymous request to the responder address returned HTTP 401. That result is evidence for this artifact, not a promise that every closed form returns the same status code.
Sources: API changes to Google Forms. Publish and manage responders on the form.
Save a short review checklist
Before you continue, you should be able to confirm:
- [ ] correct title;
- [ ] exactly four questions;
- [ ] correct order and question types;
- [ ] correct required fields;
- [ ] email collection matches the plan;
- [ ] publication state reviewed;
- [ ] published-reader permissions reviewed;
- [ ] no real respondent data was used.
Four common mistakes
- Trying to add questions in the initial
createcall. Create the shell first, then usebatchUpdate. - Sending item indexes in the wrong order. Add 0 before 1, 1 before 2, and so on.
- Trusting a successful mutation response. Always fetch the saved version with
forms.get. - Treating "unpublished" and "no responder access" as the same control. Check both Forms state and Drive permissions.
What to do after the form passes review
This guide stops before publication. A later workflow can add named responders, open and close response collection, and log every change, but that path needs its own hands-on test.
Do you create the same form after every training session? Hammer can help turn it into a repeatable workflow with scoped access, review points, and clear logging.
FAQ
Is Google Workspace CLI an officially supported Google product?
No. The project lives in Google’s googleworkspace organization on GitHub, but its README says it is not an officially supported Google product. It is also pre-v1, so always check the current version and command help.
Do I need a Google Cloud project?
Yes. The workflow requires a Cloud project, OAuth configuration, and access to the relevant Forms and Drive operations.
Can the form accept responses after this guide?
No. The walkthrough leaves the form unpublished and checks that no published-reader access remains.
Why check both the form and its Drive permissions?
The form’s publication state and published responder access are separate controls. Checking both verifies the stored content and who can reach it.
The Forge newsletter
Get new articles in your inbox
Pick the topics you care about. No noise, at most one email a week.
We follow GDPR. Unsubscribe anytime.


