Use Case - Sending a summary and follow-up Tasks to an Agent

Using Power Automate and AI for suggested follow-up tasks

At the end of a call, you want to send the summary and some suggested follow-up tasks to the Agent. The Agent can then complete the summary and choose to create the task in the CRM.

The transcription object gets fully stored in the CRM too, attached to the summary.
The transcription object gets fully stored in the CRM too, attached to the summary.

In this Use Case, you're going to address the following scenarios:

  • When the call terminates …
    • … a Transcription is saved in high German into a CRM (Salesforce).
    • … the summary is sent to the Agent as an Adaptive Card with some follow-up tasks.
  • The Agent can validate the summary and tasks. On validation, the summary and tasks are created in a CRM.
  • The transcript is attached to the summary activity in a printable format.

Before you start / Preconditions / Prerequisites / etc.

You require Tenant Admin rights to enable the following:

  • A Contact Center license is required for the features in this Use Case. Also see Nimbus Features‍ for a comparison.
  • Transcription preconditions are met and configured on the user/service - including an Azure Speech Recognizer.
  • An AI Bot service for processing is required. 💡You reuse the one deployed on Azure and test our prompt in Azure AI Foundry.
  • A CRM to store Customer data in.
  • Nimbus Power Automate Connector‍ licensing and setup preconditions are met. 💡The connector is needed to retrieve the Transcription data, alongside all necessary parsing and storage actions.

🔍 Related Note - Virtual Users: Nothing needs to be done in Nimbus, unless you do not have a Contact Center Service with Virtual Users enabled yet. Also see Use Case - Setting up a Nimbus Virtual User using Copilot for a first-time introduction.

In the Use Case below you will need to build one Power Automate flow which catches the Voice Transcription Ready Trigger Event “When the virtual user assistant has an update”. You want to call the AI service to do a summary and suggest follow-up tasks for the conversation. 

 

INC Icon Legend Accordion

Show Icon Legend 💡 = A hint to signal learnings, improvements or useful informati...

Show Icon Legend

💡 = A hint to signal learnings, improvements or useful information in context. 🔍 = Info points out essential notes or related page in context.
☝ = Notifies you about fallacies and tricky parts that help avoid problems. 🤔 = Asks and answers common questions and troubleshooting points.
❌ = Warns you of actions with irreversible / data-destructive consequence. ✅ = Intructs you to perform a certain (prerequired) action to complete a related step.
 
 

Overview of the Flow

Overview - Part 1

Overview - Part 2

 
 

Power Automate Flow

The flow has two parts: sending the Adaptive Card and second reacting to the interaction with the card.

Part 1 - Sending the Adaptive Card

You need to consider the following cases:

  1. No participants in the data, transcription is empty → in this case you will terminate the flow.
  2. The AI response was filtered and returns an error 400 → in this case you still want to save the complete transcript in Salesforce and also send an Adaptive Card informing the Agent about it.
  3. The AI response is 200 → then send the Adaptive Card.

Part 2 - Handling card interaction

Once the card has been sent, the Agent needs to interact with to create the summary and the tasks in Salesforce. Within the card, the Agent can edit the suggested summary.

  1. Save summary only → this will then create a new Call log entry in Salesforce and attach the summary (edited) and the transcription object (created in the first part) to it.
  2. Save summary with tasks → this will create the call log entry + iterate through the selected tasks to create tasks for the contact in Salesforce with the Agent as owner.
  3. Cancel: cancel the flow.

Step-by-step: Part 1

Step Screenshot
Start with Trigger Event When the Virtual user assistant has an update from the Nimbus connector.
AnothCatch the data using Flow Action Get virtual user assistant data.

Filter Array Customer

  • From=outputs('Get_virtual_user_assistant_data')?['body/Data/Participants']
  • Filter Query=@equals(@{item()?['type']},'Customer')

Filter Array Agent

  • From=outputs('Get_virtual_user_assistant_data')?['body/Data/Participants']
  • Filter Query=@equals(@{item()?['type']},'User')

Check if the Agents or Customer filter returns an empty list.

  • If yes then just terminate the flow; 
  • if no then continue with the flow.

Use the following expressions on a condition element:

  • @empty(outputs('Filter_array_Agent')?['body']) is equal to true
  • @empty(outputs('Filter_array_Customer')?['body']) is equal to true

You need to query Office 365 with Get user profile using the participant id of the Agent so that you can send the Adaptive Card in teams to the Agent.

 

  • User UPN = outputs('Filter_array_Agent')?['body'][0]['id']

You also need to query Salesforce to get the Customer information using the participant id which is the phone number for PSTN calls.

 

  • Filter Query =(MobilePhone eq '@{outputs('Filter_array_Customer')?['body'][0]['id']}') or (Phone eq '@{outputs('Filter_array_Customer')?['body'][0]['id']}') 

You save the first value of the list in a compose element

Inputs = @{outputs('Get_records_Contacts_by_phone_number')?['body/value'][0]}

☝Attention: you might want to expect and handle an empty list in case no contact was found!

 

You also need to save the Customer's full name into another compose element.

Inputs = @{outputs('Get_records_Contacts_by_phone_number')?['body/value'][0]['Name']}

 

💡We will cover the child flow further down. It basically converts the transcription data structure from

{
 "data": {
 "participants": [
  {
  "id": "5a70087c-0f8c-8765-b619-b06d6o98abfa",
  "type": "User",
  "displayName": "Axel Smith",
  "identifier": {
   "id": "5a70087c-0f8c-8765-b619-b06d6o98abfa",
   "type": "Guid"
  }
  },
  {
  "id": "+41111111111",
  "type": "Customer",
  "displayName": "+41111111111",
  "identifier": {
   "id": "+41111111111",
   "type": "Pstn"
  }
  }
 ],
 "phrases": [
  {
  "participantId": "5a70087c-0f8c-8765-b619-b06d6o98abfa",
  "startedDateTime": "2025-03-28T13:50:05.9963496Z",
  "language": "",
  "text": "Hello, to whom am I speaking?"
  },
  {
  "participantId": "+41111111111",
  "startedDateTime": "2025-03-28T13:50:17.3263496Z",
  "language": "",
  "text": "I am James Hoffman."
  },
  {
  "participantId": "5a70087c-0f8c-8765-b619-b06d6o98abfa",
  "startedDateTime": "2025-03-28T13:50:32.4063496Z",
  "language": "",
  "text": "Oh James, yes. I finished to work on your task!"
  }
 ]
 }
}

to

Axel Smith (Friday, March 28, 2025 2:50:05 PM) : Hello, to whom am I speaking?

+41111111111 (Friday, March 28, 2025 2:50:17 PM) : I am James Hoffman.

Axel Smith (Friday, March 28, 2025 2:50:32 PM) : Oh James, yes. I finished to work on your task!

 

 

Now, add a variable to the flow which will hold the system message. 

💡 Play around with your system message of your model

  • If you are using Azure AI Foundry, you can use the playground. 
  • When it is ready, copy it unto your flow.

💡 In this system message, we are using the Agent name and the Customers name variables.

Next you create the call log record in Salesforce which holds the transcription data parsed and returned ddf by our pretty print child flow.

 

You created a custom data object in Salesforce for our transcription data.

 

Initialize a variable which will be used to hold the Adaptive Card description.

Now you call the AI service using the HTTP element.

 

The body contains the following:

{
 "messages": [
 {
  "role": "system",
  "content": [
  {
   "type": "text",
   "text": "@{variables('Instructions')}"
  }
  ]
 },
 {
  "role": "user",
  "content": [
  {
   "type": "text",
   "text": "@{string(outputs('Get_virtual_user_assistant_data')?['body/Data'])}"
  }
  ]
 }
 ],
 "temperature": 0.7,
 "top_p": 0.95,
 "max_tokens": 800
}

 

Switch on the http code @{outputs('HTTP')?['statusCode']}

and go to the settings to enable run after HTTP 

  • "is successful”
  • “Has timed out” and …
  • “Has failed".

In both cases you want to create the correct Adaptive Card.

Case = 400

AdaptiveCard Inputs = {
 "type": "AdaptiveCard",
 "body": [
 {
  "type": "ColumnSet",
  "columns": [
  {
   "type": "Column",
   "items": [
   {
    "type": "Image",
    "style": "Person",
    "url": "https://luware.com/hs-fs/hubfs/Theme%20Luware/svgviewer-png-output.png",
    "size": "Small"
   }
   ],
   "width": "auto"
  },
  {
   "type": "Column",
   "items": [
   {
    "type": "TextBlock",
    "weight": "Bolder",
    "text": "Luware Nimbus",
    "wrap": true
   },
   {
    "type": "TextBlock",
    "spacing": "None",
    "text": "@{triggerOutputs()?['body/serviceName']}",
    "isSubtle": true,
    "maxLines": 1,
    "wrap": true
   }
   ],
   "width": "stretch"
  }
  ]
 },
 {
  "type": "TextBlock",
  "text": "Your call with @{outputs('Get_records_Contacts_by_phone_number')?['body/value'][0]['Name']}",
  "wrap": true,
  "weight": "Bolder",
  "style": "heading",
  "spacing": "Large",
  "separator": true
 },
 {
  "type": "Input.Text",
  "value": "I could not create a summary of the call due to the following error: @{body('HTTP')?['message']}",
  "id": "summary",
  "wrap": true,
  "isMultiline": true,
  "isRequired": true,
  "errorMessage": "Please enter a text",
  "label": "Summary",
  "maxLength": 500
 },
 {
  "type": "TextBlock",
  "text": "500 characters max.",
  "wrap": true,
  "size": "Small",
  "weight": "Lighter",
  "horizontalAlignment": "Right"
 },
 {
  "type": "ActionSet",
  "actions": [
  {
   "type": "Action.Submit",
   "id": "cancel",
   "title": "Cancel"
  },
  {
   "type": "Action.Submit",
   "title": "Log",
   "id": "log",
   "style": "positive"
  }
  ],
  "horizontalAlignment": "Right"
 }
 ],
 "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
 "version": "1.5"
}

 

 

Case = 200

You parse the message from the AI using 

  1. Parse JSON on @{body('HTTP')}
  2. Parse JSON 1 on @{outputs('HTTP')?['body']['choices'][0]['message']['content']}

 

Then you add the Adaptive Card content:

{
 "type": "AdaptiveCard",
 "body": [
 {
  "type": "ColumnSet",
  "columns": [
  {
   "type": "Column",
   "items": [
   {
    "type": "Image",
    "style": "Person",
    "url": "https://luware.com/hs-fs/hubfs/Theme%20Luware/svgviewer-png-output.png",
    "size": "Small"
   }
   ],
   "width": "auto"
  },
  {
   "type": "Column",
   "items": [
   {
    "type": "TextBlock",
    "weight": "Bolder",
    "text": "Luware Nimbus",
    "wrap": true
   },
   {
    "type": "TextBlock",
    "spacing": "None",
    "text": "@{triggerOutputs()?['body/serviceName']}",
    "isSubtle": true,
    "maxLines": 1,
    "wrap": true
   }
   ],
   "width": "stretch"
  }
  ]
 },
 {
  "type": "TextBlock",
  "text": "Your last call with @{outputs('Get_records_Contacts_by_phone_number')?['body/value'][0]['Name']}",
  "wrap": true,
  "weight": "Bolder",
  "style": "heading",
  "spacing": "Large",
  "separator": true
 },
 {
  "type": "Input.Text",
  "value": "@{body('Parse_JSON_1')?['Summary']}",
  "id": "summary",
  "wrap": true,
  "isMultiline": true,
  "isRequired": true,
  "errorMessage": "Please enter a text.",
  "label": "Summary",
  "maxLength": 500
 },
 {
  "type": "TextBlock",
  "text": "500 characters max.",
  "wrap": true,
  "size": "Small",
  "weight": "Lighter",
  "horizontalAlignment": "Right"
 },
 {
  "type": "TextBlock",
  "text": "Suggested Follow-up tasks",
  "wrap": true,
  "style": "heading"
 },
 {
  "type": "Input.ChoiceSet",
  "id": "choices",
  "style": "expanded",
  "choices": @{body('Parse_JSON_1')?['Suggested Tasks']},
  "isMultiSelect": true
 },
 {
  "type": "TextBlock",
  "text": "I suggest a max. number of 2 tasks.",
  "wrap": true,
  "size": "Small",
  "weight": "Lighter",
  "horizontalAlignment": "Right"
 },
 {
  "type": "ActionSet",
  "actions": [
  {
   "type": "Action.Submit",
   "title": "Cancel",
   "id": "cancel"
  },
  {
   "type": "Action.Submit",
   "title": "Save",
   "id": "save",
   "style": "positive"
  }
  ],
  "horizontalAlignment": "Right"
 }
 ],
 "$schema": "https://adaptivecards.io/schemas/adaptive-card.json",
 "version": "1.5"
}

 

 

 

 

Now you can post the Adaptive Card to the Agent and wait for a response.

This flow part will send and display the Adaptive Card to the last connected user with summary and follow-up tasks or, if the summary failed, then display the error message without any follow-up tasks.

Step-by-step: Part 2

Step Screenshot

Switch on the SubmitActionId 

@{body('Post_adaptive_card_and_wait_for_a_response')?['submitActionId']}

There are 3 switch cases:

  • save;
  • log and …
  • cancel.

Case: cancel 
AND

Case: default

  1. Simply add a Terminate action to the flow 
  2. Set the status to Cancelled.

Case: log

Create the record in Salesforce and map the fields to it. Here a code view output:

  "inputs": {
  "parameters": {
   "table": "Task",
   "item/WhoId": "@outputs('ContactDetails')?['Id']",
   "item/WhatId": "@outputs('ContactDetails')?['AccountId']",
   "item/Subject": "Inbound Call Log",
   "item/Status": "Completed",
   "item/Priority": "Normal",
   "item/OwnerId": "@outputs('ContactDetails')?['OwnerId']",
   "item/Description": "No summary available\n\n",
   "item/CallType": "Inbound",
   "item/TaskSubtype": "Call",
   "item/Transcription__c": "@outputs('Create_record')?['body/Id']"
 },

Then Post message in a chat or channel to the Agent.

with the message (code view):

<p class="editor-paragraph"><a href="https://luware--sales.sandbox.lightning.force.com/lightning/r/Task/@{outputs('Create_record_-_Call_log_without_summary')?['body/Id']}/view" class="editor-link">Log with transcription</a> created.</p>

Case: save

  1. You need to catch the submitted data in a Compose element:
    Inputs = @{body('Post_adaptive_card_and_wait_for_a_response')?['data']}
  2. Then you create the record; this time you add the summary to it:
 "inputs": {
 "parameters": {
  "table": "Task",
  "item/WhoId": "@outputs('ContactDetails')?['Id']",
  "item/WhatId": "@outputs('ContactDetails')?['AccountId']",
  "item/Subject": "Inbound Call Summary",
  "item/Status": "Completed",
  "item/Priority": "Normal",
  "item/OwnerId": "@outputs('ContactDetails')?['OwnerId']",
  "item/Description": "@{body('Post_adaptive_card_and_wait_for_a_response')?['data/summary']}\n\n \n\n",
  "item/CallType": "Inbound",
  "item/TaskSubtype": "Call",
  "item/Transcription__c": "@outputs('Create_record')?['body/Id']"
 },

You follow up with post a message to the Agent with the following message (code view):

<p class="editor-paragraph"><a href="https://luware--sales.sandbox.lightning.force.com/lightning/r/Task/@{outputs('Create_record_-_Call_log_with_summary')?['body/Id']}/view" class="editor-link">Log with call summary</a> created.</p>

Now you need to check if the Agent checked the tasks (choices) in the Adaptive Card to create tasks.

Condition

@{coalesce(body('Post_adaptive_card_and_wait_for_a_response')?['data/choices'],'IsNull')}

is not equal to

IsNull

If TRUE then you 

Apply to each on 

@{split(body('Post_adaptive_card_and_wait_for_a_response')?['data/choices'], ',')}

 

 

 

 

Filter array

From = @{body('Parse_JSON_1')?['Suggested Tasks']}

Filter Query = 

@{item()['value']}

is equal to

@{items('Apply_to_each')}

For each (task) …

Text = @{outputs('Filter_array')['body']}

…. you create a the task subject using a Compose element.

Inputs = Creating a task for @{items('For_each')['title']}@{item()['value']}

Now you create the record in Salesforce.

Here is the code view for the inputs:

 "inputs": {
 "parameters": {
  "table": "Task",
  "item/WhoId": "@outputs('Create_record_-_Call_log_with_summary')?['body/WhoId']",
  "item/WhatId": "@outputs('Create_record_-_Call_log_with_summary')?['body/WhatId']",
  "item/Subject": "@item()['title']",
  "item/Status": "Not Started",
  "item/OwnerId": "@outputs('Create_record_-_Call_log_with_summary')?['body/OwnerId']",
  "item/CallType": "Inbound",
  "item/TaskSubtype": "Task"
 },

 

On success you send a message with the link to the task in Salesforce to the Agent.

Message (code view) = 

<p class="editor-paragraph">Task<a href="https://luware--sales.sandbox.lightning.force.com/lightning/r/Task/@{outputs('Create_record_-_Tasks')?['body/Id']}/view" class="editor-link"> @{item()['title']}</a> created.</p>

Terminate with Success.

☝Attention: You also want to terminate with succeeded if no one interacted with the cards so the flow does not fail.

Also set run after to

 

Table of Contents