Live training: next batches coming soon. Join the waitlist:Waitlists open:Developer Training →Developer →Copilot Studio Training →Copilot Studio →

How to Get Dataverse Created By in Power Automate (Name, Email and Date)

The SPGuides service desk manager asked for a small change to the new-request email: show who logged the request. Every Service Request row in Dataverse already has a Created By column, so I expected one click in dynamic content.

It wasn’t that simple. The Created By value a flow sees is a long GUID, not a person’s name. Paste it into an email and the manager gets something like facfffb5-…, which tells nobody anything.

In this tutorial, we’ll get Dataverse Created By in Power Automate three tested ways: the formatted value that List rows already returns, an Expand Query on createdby, and a lookup in the Users table.

We’ll also format Created On as a US date, handle Modified By and Owner, and fix the errors I hit along the way.

Quick answer: In List rows, add _createdby_value to Select columns, then read the name with first(outputs('List_rows')?['body/value'])?['_createdby_value@OData.Community.Display.V1.FormattedValue']. No extra action is needed. For more user fields, set Expand Query to createdby($select=fullname) or pass the GUID to Get a row by ID on the Users table.

Why does Created By come back as a GUID?

Created By is a lookup column. It points to a row in the Users table (logical name systemuser), and Dataverse stores only that row’s ID. If you’re new to tables and lookups, my Microsoft Dataverse overview explains how they fit together.

In the Web API the lookup is exposed as _createdby_value. A GUID (globally unique identifier) is the 36-character ID of the user who created the row. Here’s what our List rows action returned for one request:

"_createdby_value": "facfffb5-....",
"_createdby_value@Microsoft.Dynamics.CRM.lookuplogicalname": "systemuser",
"_createdby_value@OData.Community.Display.V1.FormattedValue": "Bijay Kumar Sahoo"

The last line is the useful one. The Dataverse connector asks for all annotations, so Dataverse sends a formatted value with each lookup: the primary name of the related row. For a user, that’s the full name.

Build the test flow step by step

I used an instant flow so I could run it on demand. The same actions work after any trigger. If you want a refresher on the actions, see how List rows works in Dataverse flows.

  1. In Power Automate, select Create > Instant cloud flow, name it Service Requests – Get Created By, pick Manually trigger a flow and select Create.
  2. Add the Microsoft Dataverse action List rows and set Table name to Service Requests.
  3. Open Advanced parameters and turn on Select columns, Filter rows and Expand Query.
  4. Select columns: cr85a_subject,cr85a_requestnumber,createdon,_createdby_value. Filter rows: cr85a_subject eq 'Projector Setup'.
  5. Expand Query: createdby($select=fullname). You need it only for Method 2.
Power Automate List rows advanced parameters with Expand Query set to createdby select fullname

Code view shows the same settings as JSON. Your table’s prefix (cr85a_ here) will be different:

{
  "entityName": "cr85a_servicerequests",
  "$select": "cr85a_subject,cr85a_requestnumber,createdon,_createdby_value",
  "$filter": "cr85a_subject eq 'Projector Setup'",
  "$expand": "createdby($select=fullname)"
}
List rows Code view showing select with _createdby_value and expand createdby fullname for Service Requests

Then I added Compose actions for each method and one Get a row by ID. The run on 10/23/2026 succeeded in 539 ms with every action green:

Power Automate run of the Get Created By flow with every Dataverse and Compose action succeeded

Method 1: Read the formatted value (no extra action)

This is the route I use most. List rows returns an array, even for one row, so first() picks the row, and the annotation name goes in square brackets. First, the GUID itself, which you’ll need for Method 3:

first(outputs('List_rows')?['body/value'])?['_createdby_value']

Now the name. Add a Compose action named Created By Name Formatted with this expression:

first(outputs('List_rows')?['body/value'])?['_createdby_value@OData.Community.Display.V1.FormattedValue']

For the Projector Setup request (REQ-1008), the output was Bijay Kumar Sahoo:

Compose Created By Name Formatted run output showing the full name from the Dataverse formatted value

The same annotation trick returns choice labels too. I use it in getting a Dataverse choice column value.

Method 2: Use Expand Query on createdby

Expand Query joins the related Users row into the List rows result. createdby is the single-valued navigation property behind the lookup, and $select=fullname keeps the payload small. Read it like this:

first(outputs('List_rows')?['body/value'])?['createdby/fullname']

It returned the same Bijay Kumar Sahoo. Pick this method when you need more than the name, such as createdby($select=fullname,internalemailaddress) for the user’s primary email. That email field is an option we didn’t print in our run.

Method 3: Look up the user in the Users table

This is the classic way, and it still helps when another action needs the full user record. Add Get a row by ID in Power Automate, set Table name to Users and Row ID to the Created By ID output. Code view:

{
  "entityName": "systemusers",
  "recordId": "@outputs('Created_By_ID')",
  "$select": "fullname,firstname,lastname"
}
Get a row by ID Code view on the systemusers Users table with recordId from the Created By ID output

Then read the name with this expression:

outputs('Get_a_row_by_ID')?['body/fullname']
Compose Created By Name Users Table output showing the full name from the Dataverse Users table

Add internalemailaddress to Select columns when the email has to go to the creator. It costs one more API call per row, so in a loop over hundreds of rows, Method 1 or 2 is faster.

Which method should you use?

MethodExtra actionReturnsBest for
Formatted valueNoneFull nameNames in emails, Teams posts, logs
Expand QueryNoneAny user column you selectName plus email in one call
Get a row by ID (Users)1 per rowWhole user recordWhen later steps need the user row

Show Created On as a US date

Managers usually want the date next to the name. Created On comes back in UTC, so format it with formatDateTime(). More patterns are in my formatDateTime() guide:

formatDateTime(first(outputs('List_rows')?['body/value'])?['createdon'], 'MM/dd/yyyy hh:mm tt')
Compose Created On US Format output showing 10/20/2026 11:02 AM from the Dataverse createdon column

That 11:02 AM is UTC. For local time, use convertFromUtc() with a Windows time zone name, as covered in converting time zones in Power Automate:

convertFromUtc(first(outputs('List_rows')?['body/value'])?['createdon'], 'Eastern Standard Time', 'MM/dd/yyyy hh:mm tt')

Modified By, Owner and the row trigger

Modified By works the same way: _modifiedby_value, its formatted value, or modifiedby($select=fullname) in Expand Query. Owner is different, because a team can own a row as well as a user.

Check _ownerid_value@Microsoft.Dynamics.CRM.lookuplogicalname first. It returns systemuser or team, and only a systemuser GUID works with Get a row by ID on the Users table.

With the trigger When a row is added, modified or deleted, set Change type to Added and Scope to Organization, and Created By (Value) in dynamic content is the same GUID. To save it back on the row, see updating a Dataverse row in Power Automate.

Common errors and how to fix them

  • The expression returns null: _createdby_value isn’t in Select columns, or the annotation name is misspelled. Copy the key from the List rows raw output.
  • Inside Apply to each: use items('Apply_to_each')?['_createdby_value@OData.Community.Display.V1.FormattedValue'] instead of first(), with your loop’s name.
  • Wrong time in the email: createdon is UTC. Wrap it in convertFromUtc() before you format it.

For more connector walkthroughs, browse the Power Automate tutorials or see how to add a new row to Dataverse with Power Automate.

If you’re building more Dataverse flows for your team, these guides pick up from here:

Frequently Asked Questions

How do I get the Created By name from Dataverse in Power Automate?

Add _createdby_value to Select columns in List rows, then read the _createdby_value@OData.Community.Display.V1.FormattedValue key from the row. It holds the user’s full name, so no extra action is needed.

How do I get the email of the user who created a Dataverse row?

Use Expand Query createdby($select=fullname,internalemailaddress) in List rows, or pass Created By (Value) to Get a row by ID on the Users table and select internalemailaddress.

Why does Created By show a GUID in Power Automate?

Created By is a lookup to the Users table, and Dataverse stores only the user’s ID. Read the formatted value or expand createdby to get the name.

Does this work for Modified By and Owner?

Yes. Use _modifiedby_value or modifiedby for Modified By. For Owner, check _ownerid_value@Microsoft.Dynamics.CRM.lookuplogicalname first, because a team can own the row.

Where does Microsoft document formatted values?

Microsoft explains formatted values and lookup annotations in Select columns using OData, and the Expand Query field in Use lists of rows in flows.