Most automated emails send a link and hope someone clicks it. Actionable messages let you put the information, and later the buttons, directly inside the email. In this post I build a simple example from start to finish: a SharePoint list of team members shown as a table-style card inside Outlook, sent in a single email. Then I cover the step most tutorials skip: registering a provider so you get the originator ID that makes the card appear.
This is part one of a series. This post is display only. The next post adds a comments box and a Submit button that calls a Power Automate flow using an HTTP action.
What you will build
An email that shows every item in a SharePoint list (name, age and work type) as rows in an adaptive card. One email, all items, no links to click.
How it works
There are three pieces:
1. The card. JSON that describes the layout. It is embedded in the email's HTML inside a <script type="application/adaptivecard+json"> tag.
2. The originator ID. A GUID for a provider you register with Microsoft. Outlook uses it to decide whether the sender is allowed to send cards.
3. The email. Sent from Power Automate with the card JSON in the HTML body. Any normal text in the body is a fallback for clients that can't show the card.
If the originator ID is missing, wrong, or not approved for the recipient, the email still arrives but shows only the fallback text. That is the number one reason people think their card is broken.
What you need
• A Microsoft 365 mailbox on Exchange Online for the sender, and Exchange Online mailboxes for the recipients
• A SharePoint list and Power Automate
• Access to the Actionable Email Developer Dashboard
• An Exchange administrator, if you want to send to people other than yourself and a few test users
Step 1: Create the SharePoint list
Create a list called Sample Adaptive Card with these columns:
Column | Type |
Name (the default Title column) | Single line of text |
Age | Number |
Work Type | Choice: On Site, Remote, Hybrid |
Add a handful of rows. Check each column's internal name in the list settings, because the flow uses internal names. In this post I assume Title, Age and WorkType.
Step 2: Get your originator ID
Do this before building the flow, because the card needs the ID.
1. Sign in to the Actionable Email Developer Dashboard at https://outlook.office.com/connectors/oam/publish.
2. Choose to create a new provider.
3. Fill in the registration form. The labels can vary slightly, but you will provide:
o A name for the provider, such as "Team List Demo".
o The sender email address. This must be exactly the address the email will be sent from. If the flow uses your own mailbox connection, use your address. Messages from any other sender are not processed.
o Target URLs. These are the HTTPS URLs your card's buttons will call. This post has no buttons, but you will add one in part two, so plan for the URL of that flow. You can edit the provider later, and changes may need approval again.
o The scope (see below).
4. If asked whether this is a Power Automate scenario, choose Yes when your buttons will call a Power Automate HTTP trigger. Microsoft's documentation says this sets the token type to the Entra-based option automatically, and the target URL field can use a regular expression.
5. Submit the registration. The dashboard then shows your provider with a Provider ID, a GUID. That GUID is your originator ID. Copy it.
Choosing a scope
Scope | Who can receive the card | Approval |
Test users ("Sent to me") | Only the addresses you list, in your own organization | Automatic |
Organization | Anyone in your Microsoft 365 organization | An Exchange administrator in your tenant must approve it |
Global | Any Microsoft 365 user | Reviewed by Microsoft |
Start with test users while you build. When you are ready to send to others, create or change the provider to Organization scope. Exchange administrators approve these in the Actionable Messages admin dashboard at https://outlook.office.com/connectors/oam/admin. Until the status shows approved, people outside your test list will see only the fallback text.
One gotcha: if you create a new provider for Organization scope, it gets a new originator ID. Update the GUID in your flow, or the card will keep working only for you.
Step 3: Build the flow
Create a flow with a manual trigger so you can test it. The whole flow is six actions, and you do not need an Apply to each. A Select action already loops over every item, so one run produces one email with all rows.
3.1 Get items
Add the SharePoint Get items action and point it at your list.
3.2 Select: Select Card Rows
• From: body('Get_items')?['value']
• Map: switch the map to the Expression tab and paste the following. Building the row as an expression avoids the designer rejecting escaped JSON.
concat(
'{"type":"ColumnSet","separator":true,"columns":[',
'{"type":"Column","width":"2","items":[{"type":"TextBlock","text":"', replace(string(item()?['Title']),'"','\"'), '"}]},',
'{"type":"Column","width":"1","items":[{"type":"TextBlock","text":"', string(item()?['Age']), '"}]},',
'{"type":"Column","width":"2","items":[{"type":"TextBlock","color":"',
if(equals(item()?['WorkType']?['Value'],'Remote'),'Good',if(equals(item()?['WorkType']?['Value'],'Hybrid'),'Warning','Accent')),
'","text":"', string(item()?['WorkType']?['Value']), '"}]}',
']}'
)
Each item becomes one string of JSON describing a row. The color value gives a visual cue: green for Remote, amber for Hybrid, blue for On Site. Outlook cards cannot draw real colored pills, so text color is the closest option.
3.3 Compose: Compose JSON
In the Compose action, use the Expression tab and paste this. Replace the GUID with your own originator ID.
concat(
'{"type":"AdaptiveCard","version":"1.0","originator":"YOUR-ORIGINATOR-ID","body":[',
'{"type":"TextBlock","size":"Large","weight":"Bolder","text":"Sample Adaptive Card"},',
'{"type":"TextBlock","wrap":true,"isSubtle":true,"text":"Here is the current team list."},',
'{"type":"ColumnSet","columns":[{"type":"Column","width":"2","items":[{"type":"TextBlock","weight":"Bolder","text":"Name"}]},{"type":"Column","width":"1","items":[{"type":"TextBlock","weight":"Bolder","text":"Age"}]},{"type":"Column","width":"2","items":[{"type":"TextBlock","weight":"Bolder","text":"Work Type"}]}]},',
join(body('Select_Card_Rows'), ','),
']}'
)
The join call stitches all the row strings together with commas. Notice the card uses version 1.0 elements only. Outlook's actionable messages do not support newer elements such as the Table element, which is why the layout is built from column sets.
3.4 Compose: Compose HTML
Type this as plain text, not an expression:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="application/adaptivecard+json">@{outputs('Compose_JSON')}</script>
</head>
<body>
<p>Here is the current team list.</p>
</body>
</html>
The action name in outputs('Compose_JSON') must match your Compose action. Power Automate replaces spaces in action names with underscores.
3.5 Send an email (V2)
• To: your own address first
• Subject: Sample Adaptive Card
• Body: the output of Compose HTML, with Is HTML set to Yes. If the card does not show up, paste the HTML into the body's code view instead, so you can see what the editor does to the script tag.
The account behind the connection must match the sender you registered in Step 2.
Step 4: Test it
1. Run the flow and open the run history. Copy the output of the JSON Compose into any JSON validator. If it parses, the card is valid.
2. Open the email in Outlook on the web or the new Outlook. You should see the card with all your list items.
3. If you only see the fallback text, open the message properties. Outlook shows an Actionable Messages processing status. If it says None, Exchange did not process the message at all. Check the troubleshooting list below.
4. Always send a fresh email after you change anything about the provider. Messages already sent are not reprocessed.
Troubleshooting
Symptom | Likely cause |
Only the fallback text appears | The originator is wrong, the provider is not approved for that recipient, or the From address does not match the registered sender |
It works for you but not a coworker | The provider is still on test-user scope, or you created a new provider and the flow still uses the old ID |
Processing status says None | The script tag is missing or the JSON is invalid. Check the message source |
The JSON is invalid for one row | A name contains a double quote or a field is empty. The replace in the row expression handles quotes. Test your data |
Recipient is not on Exchange Online | Actionable messages are only processed for Exchange Online mailboxes |
Card appears in one Outlook app but not another | Try Outlook on the web first, since some clients and older versions show only the fallback |
Treat any URL that contains a key or signature as a secret. Do not paste those into screenshots or videos.
What's next
In part two I add a comments box and a Submit button. The button is an HTTP action defined in the card itself. When someone clicks it, Outlook sends a request to a Power Automate flow with the comment, and the card can update itself to show the submission was received. Subscribe or follow to catch it.
forum Discussion (0 comments)
Join the Discussion
Login to share rich-text comments and reply to other members.
No comments yet. Be the first to share your thoughts!