Skip to main content

How to Add a Column to a SharePoint List with CreateFieldAsXml in Power Automate

· 8 min read
Jagdish Kumawat
Founder @ Dewiride

A list is only useful with its columns. The REST API's createfieldasxml adds a column of any type from a short XML definition, including its internal name, whether it's required, its choices and its default value. This guide adds columns from a flow, one per item of a loop, and only when they're missing.

Prerequisites​

The example comes from the setup flow in Part 11 of the Career Development Plan series. It adds the 16 columns that Part 2 created by hand. Each list definition carries its columns as an array of names and XML definitions:

One column in the list definitions
{
"name": "JobTitle",
"schemaXml": "<Field Type='Text' Name='JobTitle' StaticName='JobTitle' DisplayName='JobTitle' Description='The job title this competency is for. Must match the job title in Microsoft 365 exactly.' Required='TRUE' MaxLength='255' Indexed='TRUE' />"
}

Step 1: Loop Over the Columns​

Add an Apply to each called For each column, over the current list's columns (How to Loop Through an Array with Apply to Each):

Expression
items('For_each_list')?['columns']

The For each column pane with Select an output from previous steps set to the columns token, inside the For each list loop

Step 2: Check Whether the Column Exists​

Inside the loop, Find the column asks for the list's fields with that internal name:

The Find the column pane with Method GET and the Uri _api/web/lists/getbytitle(' title ')/fields?$filter=InternalName eq ' name '&$select=InternalName, with the title and name tokens

Uri
_api/web/lists/getbytitle('@{items('For_each_list')?['title']}')/fields?$filter=InternalName eq '@{items('For_each_column')?['name']}'&$select=InternalName

Then a Condition called If the column is missing tests empty(body('Find_the_column')?['value']) against true (How to Check Whether a SharePoint List Exists shows the same pattern for lists).

Step 3: Create the Column​

In the condition's True branch, add Create the column:

FieldValue
MethodPOST
Uri_api/web/lists/getbytitle(' + the title + ')/fields/createfieldasxml
HeadersAccept: application/json;odata=nometadata
Content-Type: application/json;odata=verbose
Body
{
"parameters": {
"__metadata": { "type": "SP.XmlSchemaFieldCreationInformation" },
"SchemaXml": "@{items('For_each_column')?['schemaXml']}",
"Options": 25
}
}

The Create the column pane with Method POST, the Uri ending in /fields/createfieldasxml with the title token, the Accept and Content-Type headers, and the Body with parameters, __metadata type SP.XmlSchemaFieldCreationInformation, SchemaXml set to the schemaXml token and Options 25

What Options 25 Means​

Options adds up values from Microsoft's AddFieldOptions list:

ValueNameEffect
1AddToDefaultContentTypeThe column is added to the list's default content type, so it appears in the forms
8AddFieldInternalNameHintThe Name in the XML becomes the column's internal name
16AddFieldToDefaultViewThe column is shown in the default view

1 + 8 + 16 = 25. The hint, 8, is what makes the internal names match the XML, and the app reads columns by their internal names.

Write the SchemaXml​

Each column is one Field element. Microsoft's Field element reference lists every attribute. These are the ones the Career Development Plan columns use:

AttributeMeaning
TypeText, Note, Choice, Boolean, Number or DateTime
Name, StaticNameThe internal name
DisplayNameThe name people see
DescriptionThe hint under the column in forms
RequiredTRUE when a value is needed
MaxLengthThe longest text allowed
IndexedTRUE to index the column

Examples of each type, from the setup flow:

Text, required and indexed
<Field Type='Text' Name='EmployeeId' StaticName='EmployeeId' DisplayName='EmployeeId' Description='The employee&apos;s Microsoft Entra object ID. The app finds a person&apos;s responses with it.' Required='TRUE' MaxLength='255' Indexed='TRUE' />
Choice, with a default
<Field Type='Choice' Name='Status' StaticName='Status' DisplayName='Status' Description='Pending until the manager approves or declines the response.' Required='TRUE' Format='Dropdown' FillInChoice='FALSE'><CHOICES><CHOICE>Pending</CHOICE><CHOICE>Approved</CHOICE><CHOICE>Declined</CHOICE></CHOICES><Default>Pending</Default></Field>
Yes/No, defaulting to Yes
<Field Type='Boolean' Name='IsActive' StaticName='IsActive' DisplayName='IsActive' Description='Yes: part of the plan. No: removed from the plan (the row is kept for history).' Required='TRUE'><Default>1</Default></Field>
Whole number
<Field Type='Number' Name='RoleCompetencyId' StaticName='RoleCompetencyId' DisplayName='RoleCompetencyId' Description='The ID of the RoleCompetencies row this response is for.' Required='TRUE' Decimals='0' CommaSeparator='FALSE' />
Multiple lines of plain text
<Field Type='Note' Name='Response' StaticName='Response' DisplayName='Response' Description='What the employee has done to show this competency at this level.' Required='TRUE' RichText='FALSE' />
Date and time
<Field Type='DateTime' Name='SubmittedOn' StaticName='SubmittedOn' DisplayName='SubmittedOn' Description='When the employee last sent the response.' Required='TRUE' Format='DateTime' FriendlyDisplayFormat='Disabled' />

The XML uses single quotes because it sits inside a JSON string, which uses double quotes. An apostrophe inside a description is written as &apos;, as in employee&apos;s.

Check the Result​

The new RoleCompetencies list showed its four columns in the view, and its New item form had each one, with its description, and IsActive set to Yes:

The New item form of the test RoleCompetencies list with Competency, JobTitle, Level and IsActive, each with its description, and IsActive ticked Yes

I compared every column with Part 2's hand-built ones through the REST API: the internal names, types, required settings, lengths, indexes, choices, defaults and descriptions matched. After the repeat runs, the lists still had exactly their 16 columns, with no duplicates.

Common Mistakes​

MistakeWhat happensDo this instead
Leaving 8 out of OptionsSharePoint may not use your Name as the internal nameUse 25
Double quotes in the XMLThe JSON body breaksUse single quotes in the XML
An apostrophe in a descriptionThe XML attribute ends earlyWrite &apos;
Creating without checkingA rerun adds the column againCheck with Find the column first

Troubleshooting​

SymptomLikely causeFix
The column has an unexpected internal nameAddFieldInternalNameHint (8) wasn't in OptionsUse 25
The step fails with an XML errorA quote or apostrophe isn't escapedCheck the schemaXml value
The column isn't in the forms or the view1 or 16 wasn't in OptionsUse 25

FAQ​

Can I add a lookup or person column this way?​

Yes, the Field element supports them, but they need more attributes, such as the list a lookup points to. The Career Development Plan lists don't use them.

Why keep the XML in the flow's data instead of in each action?​

One Create the column action then serves every column of every list, and the definitions are all in one place.

Conclusion​

POST a Field element's XML to _api/web/lists/getbytitle('<title>')/fields/createfieldasxml as SP.XmlSchemaFieldCreationInformation, with Options 25 so the column keeps your internal name and shows in the forms and the view. Check for the column first, and the flow can run as often as you like.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.