Power Automate: body function

Power Automate: body function

by: Manuel 12 min read 0 comments Save

The "body" function does one job, and it does it everywhere. You give it the name of an action, and it hands you back what that action returned. Not the inputs you sent, not the headers, not the status code, just the "payload".

"Payload" is a fancy term for "everything" that it was generated in a raw format, meaning that we need to use other functions or actions to format the data in a way that it becomes useful.

If a Power Automate Flow reads anything from anything, there is almost certainly a "body" call somewhere in it.

Most of the time you never type it. You pick a value from the dynamic content list and the designer writes the expression for you behind the scenes.

That is precisely why it's worth understanding, because Power Automate does a lot of the heavy lifting for you, but if you want to build an expression using other functions for example, you need to undestand where to fetch the data and how it's organized.

So let's take a look on where to find it and how to use it.

Where to find it?

You can use the function anywhere an expression is supported. In the expression editor it sits under the workflow functions group, alongside the Outputs function and the triggerBody function. In practice you'll meet it most often inside a Compose action, a Condition action, or an Apply to each action.

Here's an example of a trigger body:

Do not confuse it with "outputs"

Both functions read from the same action, but they hand you different things. Outputs returns the whole envelope, which for a connector action means the status code, the headers, and the body. The "body" function returns only the body inside that envelope. If you need a header or a status code, "body" cannot help you, because it has already thrown them away.

Usage

Let's start with the shape. The "body" function takes exactly one parameter, and it's required:

body('<actionName>')
Parameter Required Type Description
actionName Yes String The name of the action whose body output you want

There's no second parameter, no default value, and no fallback. One action name in, one body out.

Microsoft's reference lists the return type as a string, but what comes back is the body exactly as the action produced it. For a SharePoint Get Items action that's an object holding an array, and for a Get User Profile action it's an object of properties. Treat the return type in the docs as a label, not as a promise about shape.

Naming the action

This is the rule that catches people. The name you pass is the action's internal name, not the label you see on the card, and the two differ in one important way. Spaces become underscores.

body('Get_items')

An action displayed as "Get items" is Get_items. One displayed as "Get user profile (V2)" is Get_user_profile_(V2). Microsoft's own error guidance is explicit about it, and adds a second rule alongside it. The name is case-sensitive, so body('get_items') will not find an action called "Get items".

Reading a value out of the body

The body on its own is rarely what you want. You want something inside it, and we get there with bracket notation:

body('Get_item')?['Title']

Let's break that down:

  • body('Get_item') returns the whole body of the action named "Get item".
  • ?['Title'] reaches into that body and pulls out the Title property.
  • The ? is safe navigation. If the body is null, or if Title isn't there, you get null back instead of a failed Flow.

Nesting works the same way, one safe step at a time:

body('HTTP')?['data']?['items']?[0]?['name']

Every level gets its own ?. Miss one and that level becomes the one that brings the run down.

How it relates to "outputs" and "actions"

Let's line them up. These three expressions return the same value:

body('Get_items')
outputs('Get_items')?['body']
actions('Get_items')?['outputs']?['body']

The "body" function is shorthand for the longer two. Reach for the Outputs function when you need the status code or the headers, and reach for "body" the rest of the time, because it says what you mean in half the characters.

Real-world examples

Counting what a query actually returned

Joana wants to know whether a SharePoint query found anything before the Flow bothers doing work. The SharePoint Get Items action puts its results in a value array, so the count is one expression:

length(body('Get_items')?['value'])

The length function counts the array, and a Condition action decides whether to carry on or stop with a Terminate action.

Grabbing the first match and nothing else

Sometimes you filter down to a single item and want to skip the loop entirely. The first function does that, and it returns null on an empty array rather than failing:

first(body('Get_items')?['value'])?['Title']

That reads the title of the first result, and gives you null if there wasn't one. No Apply to each action required, which also means no loop to debug later.

Pulling a value out of an API response

João's Flow calls an internal service at https://<insertApiHost>/<insertEndpoint> and needs one field buried in the response. If the call returns proper JSON, the connector hands it back already parsed:

body('HTTP')?['data']?['items']?[0]?['name']

If the service returns its JSON as raw text instead, run it through the json function first, with json(body('HTTP')). Do that only when the body genuinely is a string, since parsing something already parsed throws an error.

Non-intuitive behaviors

Not every action has a body

The "body" function assumes the action wrapped its result in a body. Plenty of them don't. The Compose action is the one you'll hit first, because its definition produces the composed value as the output directly, with no body envelope around it. There is simply nothing for "body" to select.

So for a "Compose" action, and for the other data shaping actions that behave the same way, use the Outputs function instead:

outputs('Compose')

The rule of thumb is simple. Actions that call an API have a body. Actions that shape data in place, like the Select action and the Filter Array action, are worth checking in the run history before you assume either way.

A skipped action doesn't fail, it returns nothing

An action finishes a run with a status such as Succeeded, Failed, Skipped, TimedOut, Cancelled, or Aborted. If your "body" call points at an action that was skipped, because it sat in the branch a Condition action didn't take, there is no body to return. The expression evaluates to null and the Flow carries on.

That is harder to catch than an error, because nothing turns red. The email goes out with a blank name in it and nobody notices. If a value is allowed to be missing, say so explicitly with the coalesce function or the empty function rather than letting a null drift downstream.

The question mark protects the fields, not the function

body('Get_item')?['Title'] looks like it's guarding the whole expression. It isn't. Safe navigation applies to the property lookups, so it protects ?['Title'] from a null body, and does nothing about the action name itself. Get the name wrong and you get an error rather than a null, because a name that doesn't resolve is a broken template rather than missing data.

Referencing an action in a parallel branch

You can write body('Some_Action') referring to an action sitting in a parallel branch, and it will look perfectly reasonable in the editor. Microsoft's flow checker flags exactly this, because a value from a parallel branch isn't guaranteed to exist when your action runs. Keep a "body" reference on the same path as the action it reads from.

Renaming an action silently breaks the reference

Rename an action from "Get items" to "Get open tickets" and every body('Get_items') in the Flow keeps pointing at a name that no longer exists. The designer does not rewrite them for you. Microsoft's guidance on fixing duplicate action names says as much, listing "update any expressions that reference the renamed action" as a step you perform by hand.

Limitations

It only reads the body, and only what came back

There is no parameter for the status code and no parameter for the headers, so if you're checking whether an HTTP call came back as a 200, Outputs is the right tool. The inputs you sent are out of reach too. They live in actions('<actionName>')?['inputs'], which matters when you're debugging and want to see what you actually asked for rather than what you got.

It cannot reach into deeply nested loops

Referencing an action from outside its scope works for a single level of nesting. Put the action inside two levels of Apply to each and the save fails with a template validation error about referencing an action nested in a foreach scope of multiple levels. The way around it is to write the value you need into a variable inside the loop with a Set variable action, then read that variable outside the loop with the Variables function.

It doesn't work on triggers

A trigger is not an action, so body('When_an_item_is_created') will not find it. Use the triggerBody function, which is the same idea pointed at the trigger and needs no name at all.

Expression size ceiling

As with every Power Automate expression, you have a ceiling of 8,192 characters. A long chain of body(...)?[...]?[...] lookups inside a larger expression eats into that faster than you'd think, so break the long ones across Compose actions.

Troubleshooting Common Errors

The Flow refuses to save with an InvalidTemplate error

Cause: The action name doesn't resolve. A space that should be an underscore is the usual reason, and wrong casing is the next one. body('Get Items') and body('get_items') both fail against an action named "Get items".

Solution: Use the internal name exactly, with underscores for spaces and the original casing. The safest route is to insert the value from the dynamic content picker once, then read the expression it generated.

body('Get_items')?['value']

Property selection is not supported on values of type Null

Cause: Something in the chain came back null and you tried to read a property off it. The action was skipped, or it succeeded but the field you want simply isn't in the body for this particular record.

Solution: Put a ? on every level of the lookup, then decide what a missing value should mean rather than hoping it never happens.

coalesce(body('Get_item')?['Title'], 'Untitled')

The Flow succeeds but the value is blank

Cause: A null that nobody checked. Safe navigation did its job and returned null quietly, so the run went green with an empty value inside it. A skipped action upstream is the classic source.

Solution: Test for the null instead of consuming it. Use the empty function inside an if function, or branch on it with a Condition action and handle the empty case on purpose.

if(empty(body('Get_item')?['Title']), 'Untitled', body('Get_item')?['Title'])

The body is a string when you expected an object

Cause: The service returned JSON but declared it as text, so the connector never parsed it. Every ?['field'] you write against it returns null, because you're indexing into a string.

Solution: Parse it once with the json function, or add a Parse JSON action with a schema so the fields show up in the dynamic content list afterwards.

json(body('HTTP'))?['name']

Recommendations

Here are some things to keep in mind when using the "body" function.

Let the picker write it, then read what it wrote

The dynamic content list generates correct action names every time, including the underscores and the casing. Pick the value, then open the expression and read it. You get a working reference and a free lesson in how the naming works.

Put the question mark on every level

body('Get_item')['Title'] works right up until the day the body is null, and then it takes the run down with it. body('Get_item')?['Title'] gives you a null you can handle. There is no case where dropping the ? is the better choice.

Name your actions before you reference them

Give an action its real name the moment you drop it on the canvas. Renaming later means hunting down every expression that mentions the old name, and the designer will not help you find them.

Reach for "outputs" when you need the envelope

Status codes and headers live outside the body. If you're writing error handling around an HTTP call inside a Scope action, use the Outputs function and keep "body" for the payload.

Compose once, reference many times

If the same nested lookup appears in five places, work it out once in a Compose action and reference that instead. When the API changes shape you edit one action rather than five expressions.

Always add a comment

Adding a comment will help others understand your expression. Say what the body is expected to contain and what should happen when it isn't there, because body('Get_items')?['value'] tells the next person what you typed, not what you meant.

Final Thoughts

The "body" function does a lot of quiet work in Power Automate. It sits behind almost every piece of dynamic content you've ever dragged onto a card, and it asks very little of you. Underscores instead of spaces, the right casing, a question mark on every lookup, and a deliberate answer for what a missing value means. Get those four right and it will hand you exactly what the action returned, on every single run.

Sources

Back to the Power Automate Function Reference

Photo by engin akyurt on Unsplash

Comments

Spotted a mistake or have a better approach? Let me know. I read and reply to every one.

💬

No comments yet

Be the first to share your thoughts on this article!

Leave a Comment

All comments are reviewed for spam before being displayed 5000 left
Replying to