Customer Copilot Studio
Neil Haddley • September 20, 2025
Business Central Customer Copilot Using Copilot Studio
I created a Business Central extension that allows Business Central users to "chat to" Customers.
I used the Global Variable and Generative AI features in Copilot studio.
The same approach could be used to allow users to chat to other Business Central entities
CustomerCopilotExtension.al
AL
1controladdin CustomerCopilotFactBox 2{ 3 RequestedHeight = 400; 4 MaximumHeight = 600; 5 VerticalStretch = false; 6 VerticalShrink = false; 7 HorizontalStretch = true; 8 HorizontalShrink = true; 9 Scripts = 'https://cdn.botframework.com/botframework-webchat/latest/webchat.js', 'CustomerCopilotFunctions.js'; 10 Stylesheets = 'CustomerCopilotStyles.css'; 11 12 event ControlReady() 13 procedure CreateSidebar(no: Text; city: Text; name: Text; blocked: Enum Microsoft.Sales.Customer."Customer Blocked"); 14} 15 16page 99101 CopilotCustListFactBoxPart 17{ 18 PageType = CardPart; 19 SourceTable = "Customer"; 20 21 layout 22 { 23 area(Content) 24 { 25 usercontrol(Sidebar; CustomerCopilotFactBox) 26 { 27 ApplicationArea = All; 28 } 29 } 30 } 31 32 trigger OnAfterGetRecord() 33 begin 34 CurrPage.Sidebar.CreateSidebar(rec."No.", rec.City, rec.Name, rec.Blocked); 35 end; 36} 37 38pageextension 99102 "Customer Copilot Cust Card" extends "Customer Card" 39{ 40 layout 41 { 42 addfirst(FactBoxes) 43 { 44 part(CopilotCustListFactBoxPart; CopilotCustListFactBoxPart) 45 { 46 ApplicationArea = all; //Basic, Suite;// all; 47 Caption = 'Customer Copilot'; 48 SubPageLink = "No." = FIELD("No."); 49 } 50 } 51 } 52 53} 54 55pageextension 99103 "Customer Copilot Cust List" extends "Customer List" 56{ 57 58 layout 59 { 60 addfirst(FactBoxes) 61 { 62 part(CopilotCustListFactBoxPart; CopilotCustListFactBoxPart) 63 { 64 ApplicationArea = all; 65 caption = 'Customer Copilot'; 66 SubPageLink = "No." = FIELD("No."); 67 } 68 } 69 70 } 71}
CustomerCopilotFunctions.js
JAVASCRIPT
1function CreateSidebar(no, city, name, blocked) { 2 var __div = document.getElementById('controlAddIn'); 3 4 (async function () { 5 6 // Specifies style options to customize the Web Chat canvas. 7 // Please visit https://microsoft.github.io/BotFramework-WebChat for customization samples. 8 const styleOptions = { 9 accent: '#00809d', 10 botAvatarBackgroundColor: '#FFFFFF', 11 botAvatarImage: 'data:image;base64,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', 12 botAvatarInitials: 'BOT', 13 userAvatarImage: 'https://content.powerapps.com/resource/makerx/static/media/user.336bbce3.svg', 14 userAvatarInitials: 'USER', 15 hideUploadButton: true, 16 primaryFont: fontFamily(['Segoe UI', 'Segoe WP', 'Segoe', 'device-segoe', 'Tahoma', 'Helvetica', 'Arial', 'sans-serif']), 17 }; 18 19 // Specifies the token endpoint URL. 20 // To get this value, visit Copilot Studio > Settings > Channels > Mobile app page. 21 const tokenEndpointURL = new URL('https://b838e0443024ea32b2f47862b85e99.03.environment.api.powerplatform.com/powervirtualagents/botsbyschema/cr74e_customerCopilot/directline/token?api-version=2022-03-01-preview'); 22 23 const locale = document.documentElement.lang || 'en'; // Uses language specified in <html> element and fallback to English (United States). 24 25 const apiVersion = tokenEndpointURL.searchParams.get('api-version'); 26 27 const [directLineURL, token] = await Promise.all([ 28 fetch(new URL(`/powervirtualagents/regionalchannelsettings?api-version=${apiVersion}`, tokenEndpointURL)) 29 .then(response => { 30 if (!response.ok) { 31 throw new Error('Failed to retrieve regional channel settings.'); 32 } 33 34 return response.json(); 35 }) 36 .then(({ channelUrlsById: { directline } }) => directline), 37 fetch(tokenEndpointURL) 38 .then(response => { 39 if (!response.ok) { 40 throw new Error('Failed to retrieve Direct Line token.'); 41 } 42 43 return response.json(); 44 }) 45 .then(({ token }) => token) 46 ]); 47 48 const directLine = WebChat.createDirectLine({ domain: new URL('v3/directline', directLineURL), token }); 49 50 // Sends "startConversation" event when the connection is established. 51 const subscription = directLine.connectionStatus$.subscribe({ 52 next(value) { 53 if (value === 2) { 54 directLine 55 .postActivity({ 56 localTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone, 57 locale, 58 name: 'startConversation', 59 type: 'event', 60 // https://www.dynamicsforcrm.com/pass-parameters-to-copilot-studio-bot-from-calling-site/ 61 value: { 62 Customer_No:no, 63 Customer_City:city, 64 Customer_Name:name, 65 Customer_Blocked:blocked 66 } 67 }) 68 .subscribe(); 69 70 // Only send the event once, unsubscribe after the event is sent. 71 subscription.unsubscribe(); 72 } 73 } 74 }); 75 76 WebChat.renderWebChat({ directLine, locale, styleOptions }, __div); 77 78 79 Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("ControlReady"); 80 81 })(); 82 83} 84 85function fontFamily(fonts) { 86 return fonts.map(font => `'${font}'`).join(', '); 87}
CustomerCopilotStyles.css
CSS
1* { 2 font-size: 10.5pt 3}






















