Share via


Publish an agent to a live or demo website

When publishing the agent to the web, you can publish to a prebuilt demo website (which you can use to share the agent with your teammates and stakeholders). You can also publish to your own live website.

Tip

When should I use the demo website and when should I use my own website?
You should use the demo website only to try out the agent and share it with your teammates or other stakeholders who want to try out the agent. It's not intended for production uses, for example, you shouldn't use it directly with customers.
You should publish and use the agent on your live website for production scenarios, such as a help agent on your help webpage for customers to interact with.

Important

This article contains Microsoft Copilot Studio preview documentation and is subject to change.

Preview features aren't meant for production use and may have restricted functionality. These features are available before an official release so that you can get early access and provide feedback.

If you're building a production-ready agent, see Microsoft Copilot Studio Overview.

A prebuilt demo website is automatically created for you when you publish your agent. Your teammates and stakeholders can use the demo website to try out the agent while you're building it. You might want to customize the demo site to guide your teammates' testing efforts. Publish the agent to your live website when it's ready for customers to use.

When should you use the demo website and when should you use your own website?

You should use the demo website only to try out your agent, or to share it with teammates and stakeholders who want to try it out. It's not intended for production use. You shouldn't give the URL to customers. Publish the agent on your live website for customers to use.

Customize the demo website

Edit the demo website's welcome message and trigger phrases as often as you like. The welcome message can explain the purpose of the testing and prompt your teammates for what to ask the agent.

  1. Open your agent and on the top menu bar, select Settings.

  2. On the side navigation pane, select Security.

  3. Select Authentication and then No authentication, and then select Save.

  4. Exit Settings and on the top menu bar, select Channels.

  5. Under Channels, select Demo website.

  6. On the Demo Website pane, under Welcome message, enter the message you want your teammates to see.

  7. Under Conversation starters, enter a list of trigger phrases.

    Trigger phrases are what initiate specific topics, so you could enter trigger phrases for topics that you want your teammates to test.

  8. Select Save.

  9. Copy the demo website URL and share it with your teammates.

Add your agent to your website

When your agent is ready for customers, add it to your website as an iframe code snippet. Your live website can be external or internal, like a SharePoint site. You can also add the agent to your Power Platform admin center.

  1. Copy the following HTML code and save it to a file named index.html. Alternatively, copy and paste the code into the w3schools.com HTML try it editor.

    
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <meta name="description" content="Contoso Web Chat Assistant" />
      <title>Contoso Sample Web Chat Test</title>
      <script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
      <script>
        let webChatInstance = null;
        let directLineUrl = null;
    
        // Replace with your token endpoint
        const tokenEndpoint = "<YOUR TOKEN ENDPOINT>";
        const styleOptions = {"accent":"#0078D4","autoScrollSnapOnPage":true,"autoScrollSnapOnPageOffset":0,"avatarBorderRadius":"7%","avatarSize":31,"backgroundColor":"#e8e9eb","botAvatarBackgroundColor":"#ffffff00","botAvatarImage":"https://powercatexternal.blob.core.windows.net/creatorkit/Assets/ChatbotLogoBlue.png","botAvatarInitials":"B","bubbleAttachmentMaxWidth":480,"bubbleAttachmentMinWidth":250,"bubbleBackground":"#f0eded","bubbleBorderColor":"#f5f5f5","bubbleBorderRadius":41,"bubbleBorderStyle":"solid","bubbleBorderWidth":1,"bubbleFromUserBackground":"#ebefff","bubbleFromUserBorderColor":"#f5f5f5","bubbleFromUserBorderRadius":41,"bubbleFromUserBorderStyle":"solid","bubbleFromUserBorderWidth":1,"bubbleFromUserNubOffset":0,"bubbleFromUserNubSize":0,"bubbleFromUserTextColor":"#242424","bubbleImageHeight":10,"bubbleImageMaxHeight":240,"bubbleImageMinHeight":240,"bubbleMessageMaxWidth":480,"bubbleMessageMinWidth":120,"bubbleMinHeight":50,"bubbleNubOffset":0,"bubbleTextColor":"#242424","emojiSet":true,"fontSizeSmall":"70%","hideUploadButton":false,"messageActivityWordBreak":"break-word","monospaceFont":"Consolas","paddingRegular":10,"paddingWide":10,"primaryFont":null,"sendBoxBackground":"#e8e9eb","sendBoxBorderTop":"solid 1px #808080","sendBoxButtonColor":"#0078d4","sendBoxButtonColorOnHover":"#006cbe","sendBoxButtonShadeBorderRadius":40,"sendBoxButtonShadeColorOnHover":"","sendBoxHeight":60,"sendBoxPlaceholderColor":"#171616","sendBoxTextColor":"#2e2d2d","showAvatarInGroup":"status","spinnerAnimationHeight":16,"spinnerAnimationPadding":12,"spinnerAnimationWidth":16,"subtleColor":"#000000FF","suggestedActionBackgroundColor":"#006FC4FF","suggestedActionBackgroundColorOnHover":"#0078D4","suggestedActionBorderColor":"","suggestedActionBorderRadius":10,"suggestedActionBorderWidth":1,"suggestedActionLayout":"flow","suggestedActionTextColor":"#FFFFFFFF","typingAnimationBackgroundImage":"url('https://wpamelia.com/wp-content/uploads/2018/11/ezgif-2-6d0b072c3d3f.gif')","typingAnimationDuration":5000,"typingAnimationHeight":20,"typingAnimationWidth":64,"userAvatarBackgroundColor":"#222222","userAvatarImage":"https://avatars.githubusercontent.com/u/8174072?v=4&size=64","userAvatarInitials":"U"};
        const backgroundImage = "";
        document.addEventListener('DOMContentLoaded', () => {
          const root = document.documentElement;
          root.style.setProperty('--primary-color', createGradient(styleOptions.accent));
          root.style.setProperty('--header-textColor', styleOptions.suggestedActionTextColor);
          if (backgroundImage) {
            const webchatElement = document.getElementById('webchat');
            webchatElement.style.backgroundImage = `url(${backgroundImage})`;
            webchatElement.style.backgroundSize = 'cover';
            webchatElement.style.backgroundPosition = 'center';
            webchatElement.style.backgroundRepeat = 'no-repeat';
            const overlay = document.createElement('div');
            overlay.className = 'webchat-overlay';
            webchatElement.appendChild(overlay);
          }
        });
        function createGradient(baseColor) {
          const r = parseInt(baseColor.slice(1,3), 16);
          const g = parseInt(baseColor.slice(3,5), 16);
          const b = parseInt(baseColor.slice(5,7), 16);
          const lighterColor = `#${Math.min(255, r+30).toString(16).padStart(2,'0')}${Math.min(255, g+30).toString(16).padStart(2,'0')}${Math.min(255, b+30).toString(16).padStart(2,'0')}`;
          const darkerColor = `#${Math.max(0, r-30).toString(16).padStart(2,'0')}${Math.max(0, g-30).toString(16).padStart(2,'0')}${Math.max(0, b-30).toString(16).padStart(2,'0')}`;
          return `linear-gradient(135deg, ${lighterColor}, ${baseColor}, ${darkerColor})`;
        }
        const environmentEndPoint = tokenEndpoint.slice(
          0,
          tokenEndpoint.indexOf("/powervirtualagents")
        );
        const apiVersion = tokenEndpoint
          .slice(tokenEndpoint.indexOf("api-version"))
          .split("=")[1];
        const regionalChannelSettingsURL = `${environmentEndPoint}/powervirtualagents/regionalchannelsettings?api-version=${apiVersion}`;
        function showChat() {
          const popup = document.getElementById("chatbot-popup");
          const openButton = document.getElementById("open-chat");
          popup.classList.add("visible");
          openButton.classList.add("hidden");
        }
        function hideChat() {
          const popup = document.getElementById("chatbot-popup");
          const openButton = document.getElementById("open-chat");
          popup.classList.remove("visible");
          openButton.classList.remove("hidden");
        }
        function createCustomStore() {
          return window.WebChat.createStore(
            {},
            ({ dispatch }) =>
              (next) =>
              (action) => {
                if (action.type === "DIRECT_LINE/CONNECT_FULFILLED") {
                  dispatch({
                    type: "DIRECT_LINE/POST_ACTIVITY",
                    meta: { method: "keyboard" },
                    payload: {
                      activity: {
                        channelData: { postBack: true },
                        name: "startConversation",
                        type: "event",
                      },
                    },
                  });
                }
                return next(action);
              }
          );
        }
        async function restartConversation() {
          try {
            if (!directLineUrl) {
              console.error("DirectLine URL not initialized");
              return;
            }
            const response = await fetch(tokenEndpoint);
            const conversationInfo = await response.json();
            if (!conversationInfo.token) {
              throw new Error("Failed to get conversation token");
            }
            const newDirectLine = window.WebChat.createDirectLine({
              ___domain: `${directLineUrl}v3/directline`,
              token: conversationInfo.token,
            });
            const webchatElement = document.getElementById("webchat");
            webChatInstance = window.WebChat.renderWebChat(
              {
                directLine: newDirectLine,
                styleOptions,
                store: createCustomStore(),
              },
              webchatElement
            );
          } catch (err) {
            console.error("Failed to restart conversation:", err);
          }
        }
        async function initializeChat() {
          try {
            const response = await fetch(regionalChannelSettingsURL);
            const data = await response.json();
            directLineUrl = data.channelUrlsById.directline;
            if (!directLineUrl) {
              throw new Error("Failed to get DirectLine URL");
            }
            const conversationResponse = await fetch(tokenEndpoint);
            const conversationInfo = await conversationResponse.json();
            if (!conversationInfo.token) {
              throw new Error("Failed to get conversation token");
            }
            const directLine = window.WebChat.createDirectLine({
              ___domain: `${directLineUrl}v3/directline`,
              token: conversationInfo.token,
            });
            webChatInstance = window.WebChat.renderWebChat(
              {
                directLine,
                styleOptions,
                store: createCustomStore(),
              },
              document.getElementById("webchat")
            );
          } catch (err) {
            console.error("Failed to initialize chat:", err);
          }
        }
        initializeChat();
      </script>
      <style>
        :root {
          --primary-gradient: var(--primary-color);
          --chat-width: 450px;
          --chat-height: 520px;
          --header-height: 56px;
          --border-radius: 16px;
          --transition-speed: 0.3s;
        }
        * {
          margin: 0;
          padding: 0;
          box-sizing: border-box;
          font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
            Roboto, sans-serif;
        }
        body {
          min-height: 100vh;
          background-color: #f3f4f6;
        }
        #chatbot-popup {
          display: none;
          position: fixed;
          bottom: 32px;
          right: 32px;
          width: var(--chat-width);
          height: var(--chat-height);
          background: white;
          border-radius: var(--border-radius);
          box-shadow: 0 18px 40px -5px rgba(0, 0, 0, 0.2),
            0 15px 20px -5px rgba(0, 0, 0, 0.1);
          overflow: hidden;
          opacity: 0;
          transform-origin: bottom right;
          transform: scale(0.95);
          transition: all var(--transition-speed) ease-in-out;
          z-index: 999;
        }
        #chatbot-popup.visible {
          display: block;
          opacity: 1;
          transform: scale(1);
        }
        #chatbot-header {
          background: var(--primary-color);
          padding: 16px 20px;
          height: var(--header-height);
          display: flex;
          justify-content: space-between;
          align-items: center;
          color: var(--header-textColor);
        }
        .header-title {
          display: flex;
          align-items: center;
          gap: 12px;
          font-size: 16px;
          font-weight: 500;
        }
        .header-buttons {
          display: flex;
          gap: 12px;
          align-items: center;
        }
        .icon-button {
          background: none;
          border: none;
          color: var(--header-textColor);
          cursor: pointer;
          padding: 8px;
          border-radius: 8px;
          display: flex;
          align-items: center;
          justify-content: center;
          transition: all 0.2s ease;
        }
        .icon-button:hover {
          color: var(--header-textColor);
          background: rgba(255, 255, 255, 0.1);
        }
        .icon-button:focus {
          outline: 2px solid rgba(255, 255, 255, 0.5);
          outline-offset: 2px;
        }
        #webchat {
          height: calc(100% - var(--header-height));
          background-color: #f9fafb;
          position: relative;
        }
        .webchat-overlay {
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          background: rgba(255, 255, 255, 0.85);
          pointer-events: none;
          z-index: 1;
        }
        #webchat > div {
          position: relative;
          z-index: 2;
        }
        #webchat .webchat__basic-transcript__content {
          white-space: pre-wrap !important;
          word-break: break-word !important;
        }
        #webchat .webchat__bubble__content {
          padding: 8px 12px !important;
        }
        #webchat .webchat__bubble {
          max-width: 85% !important;
          margin: 8px !important;
        }
        #webchat .webchat__basic-transcript__content ul,
        #webchat .webchat__basic-transcript__content ol,
        #webchat .webchat__bubble__content ul,
        #webchat .webchat__bubble__content ol {
          padding-left: 24px !important;
          margin: 8px 0 !important;
          list-style-position: outside !important;
        }
        #webchat .webchat__basic-transcript__content li,
        #webchat .webchat__bubble__content li {
          margin: 4px 0 !important;
          padding-left: 4px !important;
        }
        #open-chat {
          position: fixed;
          bottom: 32px;
          right: 32px;
          width: 64px;
          height: 64px;
          border-radius: 50%;
          background: var(--primary-gradient);
          border: none;
          cursor: pointer;
          display: flex;
          align-items: center;
          justify-content: center;
          box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
          transition: all var(--transition-speed) ease-in-out;
          z-index: 998;
        }
        #open-chat.hidden {
          opacity: 0;
          transform: scale(0.95) translateY(10px);
          pointer-events: none;
        }
        #open-chat:hover {
          transform: translateY(-4px);
          box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
        }
        #open-chat:focus {
          outline: 3px solid rgba(79, 70, 229, 0.5);
          outline-offset: 2px;
        }
        #open-chat svg {
          width: 28px;
          height: 28px;
          color: white;
          transition: transform 0.2s ease;
        }
        .main-content {
          max-width: 1200px;
          margin: 0 auto;
          padding: 48px 24px;
        }
        .main-content h1 {
          font-size: 36px;
          color: #111827;
          text-align: center;
        }
        .main-content p {
          font-size: 18px;
          color: #4b5563;
          line-height: 1.6;
          margin-bottom: 48px;
          text-align: center;
          max-width: 800px;
          margin-left: auto;
          margin-right: auto;
        }
        .content-grid {
          display: grid;
          grid-template-columns: repeat(2, 1fr);
          gap: 32px;
          margin-bottom: 32px;
        }
        .content-box {
          background: linear-gradient(135deg, #e6e6e6, #c4c4c4, #9f9f9f);
          padding: 32px;
          border-radius: 12px;
          box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
                      0 2px 4px -1px rgba(0, 0, 0, 0.06);
          min-height: 300px;
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
          text-align: center;
          position: relative;
          overflow: hidden;
        }
        .content-box::before {
          content: '';
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          height: 4px;
        }
        .content-box.featured {
          grid-column: span 2;
          min-height: 350px;
          background: linear-gradient(135deg, #e6e6e6, #c4c4c4, #9f9f9f);
          color: #000000;
        }
        .content-box h2 {
          font-size: 24px;
          margin-bottom: 16px;
          position: relative;
        }
        .content-box p {
          font-size: 16px;
          color: #6b7280;
          margin-bottom: 0;
        }
        .content-box.featured p {
          color: #000000;
        }
        @media (max-width: 768px) {
          .content-grid {
            grid-template-columns: 1fr;
          }
          .content-box.featured {
            grid-column: span 1;
          }
          .main-content {
            padding: 24px 16px;
          }
          .main-content h1 {
            font-size: 28px;
          }
          #chatbot-popup {
            width: 100%;
            height: 100%;
            bottom: 0;
            right: 0;
            border-radius: 0;
          }
        }
      </style>
    </head>
    <body>
      <div class="main-content">
        <h1>Header</h1>
        <p>Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat.</p>
        <div class="content-grid">
          <div class="content-box featured">
            <h2>Featured Content</h2>
            <p>Primary content area with custom styling and gradient background</p>
          </div>
          <div class="content-box">
            <h2>Section One</h2>
            <p>Content box with minimal design</p>
          </div>
          <div class="content-box">
            <h2>Section Two</h2>
            <p>Another content section with consistent styling</p>
          </div>
        </div>
      </div>
      <div id="chatbot-popup" role="complementary" aria-label="Chat Assistant">
        <div id="chatbot-header">
          <div class="header-title">
            <svg
              class="chat-icon"
              width="24"
              height="24"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
              aria-hidden="true"
            >
              <path
                d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
              ></path>
            </svg>
            <span>Contoso Assistant</span>
          </div>
          <div class="header-buttons">
            <button
              class="icon-button"
              id="restart-button"
              onclick="restartConversation()"
              aria-label="Restart Conversation"
            >
              <svg
                width="20"
                height="20"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                aria-hidden="true"
              >
                <path
                  d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"
                ></path>
                <path d="M3 3v5h5"></path>
              </svg>
            </button>
            <button
              class="icon-button"
              id="close-button"
              onclick="hideChat()"
              aria-label="Close Chat"
            >
              <svg
                width="20"
                height="20"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                aria-hidden="true"
              >
                <line x1="18" y1="6" x2="6" y2="18"></line>
                <line x1="6" y1="6" x2="18" y2="18"></line>
              </svg>
            </button>
          </div>
        </div>
        <div id="webchat" role="main"></div>
      </div>
      <button
        id="open-chat"
        onclick="showChat()"
        aria-label="Open Chat Assistant"
      >
        <svg
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
          aria-hidden="true"
        >
          <path
            d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
          ></path>
        </svg>
      </button>
    </body>
    </html>
    
    
  2. Retrieve the token endpoint for your agent.

  3. In index.html, at the line const tokenEndpoint = "<YOUR TOKEN ENDPOINT>";, replace the placeholder with the token endpoint for your agent.

  4. Open index.html using a modern browser (for example, Microsoft Edge) to open the agent in the custom canvas.

  5. Test the agent to ensure you're receiving responses from it and that it's working correctly.

  6. Provide the snippet (index.html) to your web developer to add the agent to your website.