Somebody ticks six boxes on a Microsoft Form. The confirmation email arrives and all six answers are crammed onto one line, wrapped in square brackets, with a quotation mark around every item. It looks broken, because it looks like this:
["Foxit PDF","Acrobat Pro","Zoom","Tableau","Slack","TeamViewer"]
What you wanted was a list. One item per line, each with its own bullet. The fix takes about ten minutes, adds one action to your flow, and changes nothing about the form itself. Nobody filling it in will notice a difference.
Applies to: any Power Automate cloud flow triggered by Microsoft Forms, where the form has a Choice question with “Multiple answers” turned on. You need the account that owns the flow, and ten minutes in which it is safe for a test email to go out.
Why it happens
A checkbox question can have more than one answer, so Forms has to hand Power Automate several values in a field that only carries one. It does that by packing them into a single piece of text formatted as a JSON array — that is what the brackets and quotation marks are.
Power Automate has no reason to think that text is special. You drop the field into an email, and it prints the text it was given, punctuation and all. Nothing is malfunctioning. The data simply never gets unpacked, because nothing in the flow asks it to be.
So that is the job: one step that unpacks the text into separate items and rebuilds it as a list, inserted between the form and the email.
Part 1: find the question’s ID
Every question on a form has an internal name that Power Automate uses behind the scenes. It is a long run of letters and numbers beginning with r, and you need the one belonging to your checkbox question.
- Sign in at make.powerautomate.com with the account that owns the flow.
- On the left, choose My flows, find your flow, and click Edit.
- Scroll to the action called Get response details. That is the step that pulls the answers out of the form.
- Directly beneath it, click + and then Add an action.
- Search for Compose and pick the result under Data Operation. A new box appears in the flow.
- Click inside the new action’s Inputs box. A panel opens listing your form questions. Click your checkbox question; a coloured tag drops into the box.
- Click Save and wait for the confirmation.
- Click the three dots on the Compose action and choose Peek code.
In the window that opens, find the line that looks like this. The long code after body/ is your Question ID:
"inputs": "@outputs('Get_response_details')?['body/rXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX']"
Copy it, starting at the r and stopping before the closing quotation mark, and paste it somewhere safe. Then close the Peek code window.
Part 2: turn the Compose action into a list builder
At the moment that Compose action just repeats the same messy line back at you. Replace its contents with an expression that rebuilds the line as a list.
- Click inside the Compose action’s Inputs box and delete the coloured tag. The box must be completely empty.
- In the panel that opens, click the Expression tab (on some screens it is labelled fx).
- Paste the expression below, replacing both copies of
PASTE_QUESTION_ID_HEREwith the ID you saved.
if(empty(outputs('Get_response_details')?['body/PASTE_QUESTION_ID_HERE']), '<p><em>None requested</em></p>', concat('<ul><li>', join(json(outputs('Get_response_details')?['body/PASTE_QUESTION_ID_HERE']), '</li><li>'), '</li></ul>'))
The ID appears twice. Replace both, or the expression will half-work and be hard to debug. Do not put an equals sign or quotation marks in front of it — paste it exactly as shown.
- Click OK (or Add). The expression collapses into a single tag inside the Inputs box.
- Rename the action so you can find it later: three dots, Rename, then something like Software List.
- Click Save.
Reading that expression in plain English, in the order it runs: check whether anyone ticked anything at all; if not, write “None requested”; if they did, split the line into separate items, wrap each one in a bullet, and close the list off.
Part 3: put the list into the email
- Open your Send an email (V2) action.
- Find the line in the body where the old answers appear. Select the whole line, including the bullet in front of it, and delete it.
- Put the cursor on the now-empty line.
- Open the dynamic content panel, scroll to the group named after your Compose action (Software List), and click Outputs.
- Click Save.
Step 2 is the one people skip. If you leave the template’s own bullet behind, you get a bullet containing a bulleted list, which looks worse than the problem you started with.
Part 4: test it before you trust it
- Click Test in the top right.
- Choose Automatically, pick a previous successful run, and click Test. That replays a real submission without bothering anyone. If no previous runs are offered, choose Manually and submit a test response yourself.
- Open the email and check that each item is on its own bulleted line.
Variations
Shorter, if the question is required. Only use this when the checkbox question cannot be left blank, because it will fail the run if nobody ticks anything:
concat('<ul><li>', join(json(outputs('Get_response_details')?['body/PASTE_QUESTION_ID_HERE']), '</li><li>'), '</li></ul>')
Plain lines instead of bullets. Each answer on its own line, no bullet:
join(json(outputs('Get_response_details')?['body/PASTE_QUESTION_ID_HERE']), '<br>')
More than one checkbox question. Repeat Parts 1 to 3 for each one, with its own Compose action, its own name and its own Question ID. Do not try to make a single Compose action handle several questions.
If something goes wrong
| What you see | What it means, and the fix |
|---|---|
The email shows the tags themselves, like <ul><li>Zoom</li> | The email body is in plain text mode. Open the Send an email action, click the </> button above the body box to switch to code view, then re-insert the Outputs tag. |
The run fails with InvalidTemplate, mentioning json | Somebody submitted the form without ticking anything. Use the longer expression that starts with if(empty(. |
| Power Automate refuses to save, saying the expression is invalid | The action name inside the expression does not match your flow. If your step is not called “Get response details”, change that part to match, with every space replaced by an underscore: “Get form answers” becomes Get_form_answers. |
| A bullet wrapped around the whole bulleted list | The template’s original bullet was not deleted in Part 3. Delete the entire line, bullet marker included, and insert the Outputs tag on a clean empty line. |
| Answers are still comma separated | The old dynamic content tag is still in the email body. Make sure the tag you inserted is the Outputs under your Compose action’s group, not the one under Get response details. |
| Only the first answer appears | The expression was pasted incompletely and is missing its join. Copy the whole thing again. |
If a test goes badly and wrong emails are going out, turn the flow off rather than trying to fix it live: My flows, the three dots next to the flow, Turn off. Fix it, then turn it back on the same way.
Got a checkbox question this does not handle, or a Forms field that arrives in some other unhelpful shape? Bring it to The Patch Panel.