Microsoft Forms Surveys
Overview
Microsoft Forms is a lightweight survey and quiz tool included in Microsoft 365. It supports choice questions, text input, rating scales, date pickers, and Likert matrices. The Graph API for Forms is available only on the beta endpoint — it is not yet in v1.0. This means endpoint paths, request bodies, and response shapes may change without notice.
Forms are well suited for small-team scenarios (up to 20 people): quick polls, event RSVPs, onboarding checklists, customer feedback, retrospective surveys, and internal quizzes.
Documentation Coverage Audit
Use /forms-coverage-audit <form-id> when users ask for full feature coverage validation. This workflow compares SKILL guidance to Microsoft Forms Graph beta documentation and live endpoint responses.
Always label beta-dependent capabilities explicitly and include mitigation steps when endpoint schemas may change.
Base URL
https://graph.microsoft.com/beta
All Forms endpoints below are relative to this base URL.
API Endpoint Reference
Forms
| Operation | Method | Endpoint |
|---|---|---|
| List user's forms | GET | /me/forms |
| Get form | GET | /me/forms/{formId} |
| Create form | POST | /me/forms |
| Update form | PATCH | /me/forms/{formId} |
| Delete form | DELETE | /me/forms/{formId} |
| List group forms | GET | /groups/{groupId}/forms |
| Create group form | POST | /groups/{groupId}/forms |
Create form body:
{
"title": "Team Retrospective — Sprint 12",
"description": "Share what went well, what didn't, and ideas for improvement"
}
Response includes id, webUrl, status (draft/active), and createdDateTime.
Questions
| Operation | Method | Endpoint |
|---|---|---|
| List questions | GET | /me/forms/{formId}/questions |
| Get question | GET | /me/forms/{formId}/questions/{questionId} |
| Add question | POST | /me/forms/{formId}/questions |
| Update question | PATCH | /me/forms/{formId}/questions/{questionId} |
| Delete question | DELETE | /me/forms/{formId}/questions/{questionId} |
Responses
| Operation | Method | Endpoint |
|---|---|---|
| List responses | GET | /me/forms/{formId}/responses |
| Get response | GET | /me/forms/{formId}/responses/{responseId} |
Responses are paginated — follow @odata.nextLink for additional pages. Default page size is 100.
Question Type Reference
Choice (Single or Multi-Select)
Use for polls, preference picks, or any closed-ended question.
{
"displayName": "Which office location do you prefer?",
"questionType": "choice",
"isRequired": true,
"orderIndex": 0,
"choiceOptions": {
"choices": [
{ "displayName": "Downtown HQ", "value": "downtown" },
{ "displayName": "Eastside Campus", "value": "eastside" },
{ "displayName": "Remote", "value": "remote" }
],
"allowMultipleAnswers": false,
"hasOtherOption": true
}
}
- Set
allowMultipleAnswers: truefor "select all that apply" behavior. - Set
hasOtherOption: trueto add a free-text "Other" option at the end.
Text (Short Answer)
Use for names, emails, or brief open-ended input.
{
"displayName": "What is your email address?",
"questionType": "text",
"isRequired": true,
"orderIndex": 1,
"textOptions": {
"isLongAnswer": false,
"maxLength": 100
}
}
Text (Long Answer)
Use for detailed feedback, comments, or explanations.
{
"displayName": "Describe what went well this sprint",
"questionType": "text",
"isRequired": false,
"orderIndex": 2,
"textOptions": {
"isLongAnswer": true,
"maxLength": 4000
}
}
Rating (Star or Number)
Use for satisfaction scores, NPS, or experience ratings.
Star rating (1-5):
{
"displayName": "Rate your overall satisfaction with the event",
"questionType": "rating",
"isRequired": true,
"orderIndex": 3,
"ratingOptions": {
"ratingScale": 5,
"ratingType": "star",
"lowScoreLabel": "Very Dissatisfied",
"highScoreLabel": "Very Satisfied"
}
}
Number rating (1-10) — useful for NPS-style questions:
{
"displayName": "How likely are you to recommend this training to a colleague?",
"questionType": "rating",
"isRequired": true,
"orderIndex": 4,
"ratingOptions": {
"ratingScale": 10,
"ratingType": "number",
"lowScoreLabel": "Not at all likely",
"highScoreLabel": "Extremely likely"
}
}
Date
Use for availability checks, scheduling, or event RSVPs.
{
"displayName": "What date works best for the offsite?",
"questionType": "date",
"isRequired": true,
"orderIndex": 5,
"dateOptions": {
"includeTime": false
}
}
Set includeTime: true if you need a time component (e.g., appointment scheduling).
Likert Scale
Use for agreement matrices, satisfaction batteries, or multi-statement evaluations.
{
"displayName": "Rate your agreement with each statement below:",
"questionType": "likert",
"isRequired": true,
"orderIndex": 6,
"likertOptions": {
"columns": [
{ "displayName": "Strongly Disagree", "value": "1" },
{ "displayName": "Disagree", "value": "2" },
{ "displayName": "Neutral", "value": "3" },
{ "displayName": "Agree", "value": "4" },
{ "displayName": "Strongly Agree", "value": "5" }
],
"rows": [
{ "displayName": "The meeting cadence is appropriate" },
{ "displayName": "I receive feedback in a timely manner" },
{ "displayName": "My workload is manageable" },
{ "displayName": "I feel included in team decisions" }
]
}
}
Common Patterns for Small Teams
Quick Team Poll
Create a single-question choice form for fast decisions:
POST /me/formswith title "Friday Lunch Poll".- Add one choice question with 3-5 options and
isRequired: true. - Share the
webUrlin a Teams channel. - After the deadline, GET responses and tally votes.
Event RSVP
- Create a form with title "Team Offsite RSVP".
- Add questions: text (name), choice (attending yes/no), date (preferred date), text (dietary restrictions).
- Mark attendance choice as required.
- Summarize responses: headcount, date preferences, special requirements.
Onboarding Checklist
- Create a form with title "New Hire Onboarding Checklist".
- Add choice questions (yes/no) for each onboarding step: laptop received, accounts created, buddy assigned, first-week training completed.
- Add a rating question for overall onboarding experience.
- Add a long-text question for suggestions.
Sprint Retrospective
- Create a form titled "Sprint 12 Retro".
- Add three long-text questions: "What went well?", "What could be improved?", "Action items for next sprint?".
- Add a rating question for sprint satisfaction (1-5 stars).
- Add a Likert question for process health (communication, planning, code review, deployment).
Customer Feedback
- Create a form titled "Product Feedback — Q1 2026".
- Add a rating question (NPS 1-10).
- Add choice questions for feature satisfaction.
- Add long-text for open-ended feedback.
- Aggregate: calculate NPS score (promoters minus detractors), feature satisfaction percentages, and common themes from text responses.
Response Aggregation
Choice Questions
Count each option, compute percentages:
Option A: 8/15 (53.3%)
Option B: 4/15 (26.7%)
Option C: 3/15 (20.0%)
Rating Questions
Compute mean, median, and distribution:
Average: 4.2 / 5.0
Median: 4
Distribution: 1★=0, 2★=1, 3★=2, 4★=6, 5★=6
For NPS (1-10 scale):
- Promoters (9-10), Passives (7-8), Detractors (0-6).
- NPS = %Promoters - %Detractors.
Likert Questions
Compute average score per row statement:
"Meeting cadence is appropriate" Avg: 4.1
"Feedback is timely" Avg: 3.5
"Workload is manageable" Avg: 3.8
Text Questions
For small teams (up to 20), list all responses verbatim. For larger sets, show the first 10 and a count of remaining.
Authentication & Permissions
| Permission | Type | Description |
|---|---|---|
Forms.Read |
Delegated | Read forms, questions, and responses |
Forms.ReadWrite |
Delegated | Create and modify forms, add/edit questions |
These are delegated permissions — they act on behalf of the signed-in user. Application permissions for Forms are not yet available in the beta API.
Token Acquisition
Use @azure/identity with ClientSecretCredential or InteractiveBrowserCredential:
const { ClientSecretCredential } = require("@azure/identity");
const { Client } = require("@microsoft/microsoft-graph-client");
const { TokenCredentialAuthenticationProvider } = require("@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials");
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
scopes: ["https://graph.microsoft.com/.default"]
});
const graphClient = Client.initWithMiddleware({ authProvider });
// Use beta endpoint
const forms = await graphClient.api("/me/forms").version("beta").get();
Quiz Mode
Forms supports a quiz mode that adds scoring, correct answers, and feedback to questions.
Create a quiz:
POST /me/forms
{
"title": "Cloud Fundamentals Quiz",
"description": "Test your knowledge of Azure basics",
"isQuiz": true
}
Quiz choice question with correct answer and points:
{
"displayName": "Which Azure service provides serverless compute?",
"questionType": "choice",
"isRequired": true,
"orderIndex": 0,
"choiceOptions": {
"choices": [
{ "displayName": "Azure Functions", "value": "functions" },
{ "displayName": "Azure VMs", "value": "vms" },
{ "displayName": "Azure SQL", "value": "sql" }
],
"allowMultipleAnswers": false
},
"grading": {
"correctAnswers": ["functions"],
"points": 10
}
}
When isQuiz is true, the form displays scores to respondents after submission. Each question can have a grading block with correctAnswers (array of matching values) and points (numeric score). Text questions can also be graded manually after submission.
Form Publish API
New forms are created in draft status. To make a form available to respondents, publish it:
PATCH /me/forms/{formId}
Publish body:
{
"status": "active"
}
Unpublish (close form to new responses):
{
"status": "closed"
}
Valid status values: draft, active, closed. A form must be active for the webUrl to accept submissions.
OData Filter Examples
Responses and questions support OData query parameters for efficient data retrieval:
| Parameter | Example | Purpose |
|---|---|---|
$top |
GET /me/forms/{id}/responses?$top=25 |
Limit to first 25 responses |
$orderby |
GET /me/forms/{id}/responses?$orderby=submitDateTime desc |
Sort by newest first |
$filter |
GET /me/forms/{id}/responses?$filter=submitDateTime ge 2026-03-01T00:00:00Z |
Responses after a date |
$select |
GET /me/forms/{id}/responses?$select=submitDateTime,respondent |
Return only specific fields |
$skip |
GET /me/forms/{id}/responses?$skip=100&$top=100 |
Paginate results (page 2) |
Combined example: Get the 10 most recent responses with only answers and timestamps:
GET /me/forms/{formId}/responses?$top=10&$orderby=submitDateTime desc&$select=submitDateTime,answers
Error Response Shape
Forms endpoints return the standard Graph error envelope:
{
"error": {
"code": "ItemNotFound",
"message": "The specified form was not found.",
"innerError": {
"date": "2026-03-01T12:00:00",
"request-id": "abc-123-def",
"client-request-id": "client-456"
}
}
}
Forms-specific error codes:
| Code | Status | Meaning |
|---|---|---|
ItemNotFound |
404 | Form, question, or response does not exist |
AccessDenied |
403 | User does not own the form or lacks Forms.ReadWrite |
InvalidRequest |
400 | Malformed question body (e.g., choice question with no choices) |
QuotaExceeded |
429 | Too many requests — retry after Retry-After header |
GeneralException |
500 | Server error — retry with exponential backoff |
BadGateway |
502 | Upstream service error — Forms beta can be intermittently unstable |
Branching Logic
Forms supports conditional question display based on prior answers. Branching lets you show or skip questions depending on what the respondent selected.
Branch definition (on a choice question):
{
"displayName": "Are you attending in person?",
"questionType": "choice",
"isRequired": true,
"orderIndex": 3,
"choiceOptions": {
"choices": [
{ "displayName": "Yes — in person", "value": "in-person" },
{ "displayName": "No — remote", "value": "remote" }
],
"allowMultipleAnswers": false
},
"branching": {
"rules": [
{
"matchValue": "in-person",
"targetQuestionId": "<dietary-question-id>"
},
{
"matchValue": "remote",
"targetQuestionId": "<timezone-question-id>"
}
]
}
}
Branching rules:
- Each
rulemaps amatchValue(the selected choice value) to atargetQuestionId(the next question to display). - If no rule matches, the form proceeds to the next question in
orderIndexorder. - Branching only works on choice questions (single-select).
- Use branching for event RSVPs (in-person → dietary, remote → timezone), conditional feedback (satisfied → thanks, unsatisfied → details), or skip-logic surveys.
Important Notes
- Beta only: All Forms endpoints are under
/beta. They are not available in/v1.0. - Form status: New forms are created in
draftstatus. They must be published (setstatustoactiveor use the Forms web UI) before respondents can submit. - Pagination: Response lists follow standard OData pagination. Always check for
@odata.nextLink. - Rate limits: Graph API enforces per-app and per-user throttling. Handle 429 responses with retry-after headers.
- Group forms: To create a form owned by a Microsoft 365 Group (shared editing), use
/groups/{groupId}/formsand ensure theGroup.ReadWrite.Allpermission is also granted.
Progressive Disclosure — Reference Files
| Topic | File |
|---|---|
| Forms Graph API (beta), create/update/delete forms, quiz vs survey, settings, response limits, date ranges | references/form-creation.md |
| Question types (choice, text, rating, date, ranking, Likert, NPS), branching logic, shuffle, subtitles | references/questions-branching.md |
| Get responses, individual detail, summary statistics, NPS calculation, export patterns, Power Automate trigger | references/responses-analytics.md |