Skip to content
← Developer Hub

Developer Hub

Add Raia Live Chat to a Windows desktop app

Show your Live Chat agent beside your app's screens in a VB.NET WinForms or PxPlus app, using a hosted HTTPS page in an embedded browser.

Want your support agent beside the screen your users already work in? This guide puts raia Live Chat inside a Windows desktop app, so someone on a billing screen can ask a question and read the answer without switching windows.

The chat is a web page shown inside your app. You host one small HTTPS page that loads raia Live Chat in a full-size frame, and your app displays that page in an embedded browser. This is not a native .NET SDK, and it does not let the agent see or control your desktop app.

What you are building

text
Your desktop app (WinForms + WebView2, or PxPlus *BROWSER)
        │  shows one HTTPS page and calls small helpers on it
        ▼
Your wrapper page, for example https://support.example.com/raia-chat.html
        │  loads raia Live Chat in an iframe through the raia iframe SDK
        ▼
raia Live Chat for your agent (raiabot.raia2.com)
PieceWho owns itWhat it does
The agent and its Live Chat settingsraia administratorTurns on Live Chat, makes the agent Public and sets optional security.
The wrapper pageWhoever runs your web hostingLoads the chat and offers a few helpers the app can call.
The desktop app codeA desktop developerShows the page, sends the signed-in user's name, handles links and sign-out.

The raia administrator steps need no coding. The wrapper and desktop steps need a developer who can edit, build and test the app.

Before you begin

You need:

  • Permission to edit an agent in raia, or a raia administrator who will do Step 1 for you. Start with a demo agent meant for testing.
  • A place to host one static HTML file at an HTTPS address you control.
  • For WinForms: a VB.NET WinForms project targeting .NET Framework 4.6.2 or later, the Microsoft.Web.WebView2 NuGet package, and the Microsoft Edge WebView2 Runtime on each Windows PC. Use Windows versions that Microsoft's WebView2 documentation supports.
  • For PxPlus: a PxPlus version that includes the *BROWSER object (added in PxPlus 2017). A current release is recommended. Check syntax against the manual for your version.
  • The starter files: Download the desktop chat starter (ZIP). It contains one top-level folder, raia-desktop-chat-starter, with a README.md that lists every file and value to change.

The starter is a docs adaptation of a sample supplied to raia, not a tested production library. Test it on the PCs you support before rollout.

Step 1: Set up the agent in raia

A raia administrator does these steps.

  1. Sign in to raia2.com, open Launch Pad, then Agents, and choose your agent. Open Skills and confirm the Live Chat skill is enabled.

Open Agents, choose your agent, then open Skills. These views locate the navigation; follow the skill-specific settings below.

In Launch Pad, open Agents and choose the agent you want to configure.
  1. Highlight 1: Agents
On that agent, open Skills.
  1. Highlight 1: Skills
  1. Open the agent's Launch tab. Under Agent Status, choose Public. Before you click: Public makes the agent's public chat link and embed code usable by anyone who has them, so use the demo agent you intend to test with first. Live Chat does not work for Private agents.
  2. On the same tab, click View Embed Code and copy the public agent ID shown in the embed code. This ID is not the Live Chat security key and not an Agent Secret Key.
The agent's Launch tab. This capture shows Agent Status set to PRIVATE: it shows where the control is, not a Public agent. Choose Public for your demo agent.
  1. Highlight 1: Launch tab.
  2. Highlight 2: View Embed Code: copy the public agent ID from the embed code.
  3. Highlight 3: Agent Status: this capture shows PRIVATE. Choose PUBLIC for your demo agent.
  1. Optional: open the Live Chat skill's Security tab and create a security key. If you turn one on, the wrapper page must send it with INIT. The key is visible to anyone who opens the page, so it is not a server secret. Never put an Agent Secret Key or an organization API key in the page or the app.
  2. If your account shows an allowed-origins setting on that same Security tab, add the exact origin of your wrapper page and click Save. An origin is the scheme, host and port together, for example https://support.example.com. Use this Live Chat setting, not Settings → Domains, which controls invitation domains. If your account does not show an allowed-origins setting, you can continue with the basic setup without that restriction, but do not treat the chat as limited to your page until you have checked a restriction yourself.
  3. If you use IP Whitelist or Geographic restrictions, remember that chats come from your users' PCs, not from your web server. Allow the addresses and countries your users connect from.
Live Chat → Security, upper Access Control section. Security keys are further down this tab and are not shown here.
  1. Highlight 1: IP Whitelist: enter the addresses your users' PCs connect from, not your web server's.
  2. Highlight 2: Geographic restrictions: based on where your users' PCs connect from.

Nothing needs to be provisioned by raia for this setup. Test your own settings with the steps below.

Step 2: Host the wrapper page

Whoever runs your web hosting does this step, usually with the desktop developer.

  1. Extract the ZIP and open web/raia-chat.html in a text editor.
  2. Change two values near the top of the script:
  • AGENT_ID: the public agent ID from View Embed Code.
  • SECURITY_KEY: your Live Chat security key, or leave it as "" if you did not create one.
  1. Upload the file to one HTTPS address you own, for example https://support.example.com/raia-chat.html.
  2. Open that address in Edge or Chrome on a normal desktop PC. Ask the agent a question and wait for a real reply. Do not continue until this works.

Why HTTPS and not a local file? A page opened from a file has no usable origin, so an origin restriction cannot match it. A hosted page also lets you update the chat without reinstalling the desktop app.

This is the part of the page that loads the chat. The iframeId must match the id of the <iframe> element, or the SDK cannot find the chat:

html
<iframe id="raia-chat-iframe" title="Support chat" allow="clipboard-write"></iframe>
<script src="https://raiabot.raia2.com/assets/raia-chatbot-iframe.js"></script>
<script>
  var AGENT_ID = "YOUR_AGENT_ID";   // public agent ID from Launch > View Embed Code
  var SECURITY_KEY = "";            // Live Chat > Security key, or "" if you don't use one
  var RAIA_ORIGIN = "https://raiabot.raia2.com";
  var iframe = document.getElementById("raia-chat-iframe");

  // The message listener is added first (see the full file), then:
  iframe.src = RAIA_ORIGIN + "/" + encodeURIComponent(AGENT_ID) + "/chat";
  var chat = new window.RaiaIframeChat({
    iframeId: "raia-chat-iframe",
    isSecurityKeyRequired: SECURITY_KEY !== ""
  });
  if (SECURITY_KEY !== "") {
    chat.sendCommand("INIT", { apiKey: SECURITY_KEY });
  }
</script>

The full file also handles a missing SDK script, reports INIT failures and accepts messages only from https://raiabot.raia2.com and from its own iframe.

Check the origin restriction

If you added an allowed origin, check that it works. With your developer, host a copy of the same wrapper page on a second origin that is not on the list, for example a test subdomain, and open it. The chat should not start there, while the listed address still works. Opening the original address from another computer does not test this, because the origin is still the listed one. Origin checks limit which web pages can start the chat. They do not identify or authenticate the person chatting.

The helpers on the wrapper page

The wrapper page creates window.raiaHost. These helpers belong to this sample, not to a new raia SDK. Each one sends a standard Live Chat command through the raia iframe SDK.

HelperCommand sentUse it for
setUser({ firstName, lastName, email, customData })SET_USERFirst name, last name, email and customData, so the agent can personalize replies.
setContext(text)SET_CONTEXTA short note such as Screen: Billing. Keep it minimal and non-sensitive.
clearUser() / clearContext()CLEAR_USER / CLEAR_CONTEXTRemove what you set earlier.
reset()RESET_CONVERSATIONStart a new conversation.
signOut(requestId)All three above, in orderSign-out on shared PCs. Reports signout-complete or signout-error.

Pass the user's details straight to the helper, like this:

js
window.raiaHost.setUser({ firstName: "Testy", lastName: "O'Brien", email: "testy@example.com", customData: { accountId: "TEST-001" } });

Do not wrap the details in a user object yourself. The helper does that when it builds the SET_USER command.

SET_USER is personalization only. It is not sign-in, identity proof or access control: any code that controls the page can send different values. Never let the agent grant access or reveal account data because of these values.

The page reports what happened back to the desktop app as small events:

EventMeaning
readyThe raia chat inside the iframe has loaded its message listener.
init-readyThe chat finished its initialization handshake.
ackThe chat accepted a command, for example ack SET_USER.
errorA command or the chat script failed.
messageA new chat message arrived.
signout-complete / signout-errorThe sign-out sequence finished, or stopped at a failed step.

None of these events is an agent reply. ready and init-ready only mean the chat is loading, and an ack only means a command was accepted. To confirm a reply, look at the chat and read it.

Where to see the events: in the WinForms sample, OnWebMessageReceived in RaiaChatForm.vb checks each message and raises the ChatEvent event. MainForm.vb handles ChatEvent and writes each event name and command name to its log text box, for example ack SET_USER. The PxPlus excerpt has no equivalent event log, so in PxPlus confirm the result in the chat itself: check that the agent uses the name you passed and gives a real reply.

Step 3: Add the chat to a WinForms app

A desktop developer does this step. There are two ways to use the sample: run it on its own first, then add the chat window to your existing app.

Try the sample on its own

The ZIP contains a small standalone app. MainForm.vb stands in for your app and has a Support chat button, a Sign out button and an event log.

  1. Target .NET Framework 4.6.2 or later. Projects on 4.6.1 or earlier must be retargeted.
  2. Add the Microsoft.Web.WebView2 NuGet package and a reference to System.Web.Extensions, which the sample uses for JSON encoding.
  3. Make sure each PC has the WebView2 Runtime. Microsoft's distribution guide explains the installer options. The sample shows a friendly message when the runtime is missing.
  4. In RaiaChatForm.vb, set ChatUrl to the exact HTTPS address of your wrapper page, and change DataFolder to your company and app names. WebView2 needs a writable per-user folder for its own files, such as one under %LocalAppData%, never Program Files.
  5. Open winforms/RaiaChatSample.vbproj, then build and run it. Click Support chat. The chat should fill the window, and the log in the main window shows events such as ready.

Add the chat to your existing app

Copy RaiaChatForm.vb into your project and complete steps 1 to 4 there. Copying the file adds the chat window class only. It does not add a button or a menu item: open the chat from your own control, for example a Help button or menu command. In that control's click handler, create the window with your signed-in user's details and keep the instance, because you need it later for sign-out:

vb
' A field on your main form, so sign-out can reach the open chat window.
Private chatForm As RaiaChatForm

Private Sub OpenSupportChat()
    If chatForm IsNot Nothing AndAlso Not chatForm.IsDisposed Then
        chatForm.Activate()
        Return
    End If
    ' Fictional values for testing. Use your signed-in user's details.
    Dim user As New RaiaChatUser With {
        .FirstName = "Testy", .LastName = "O'Brien",
        .Email = "testy@example.com", .AccountId = "TEST-001"}
    chatForm = New RaiaChatForm(user)
    chatForm.Show(Me)
End Sub

To see events in your app, handle chatForm.ChatEvent the way MainForm.vb does. When the user signs out of your app, call SignOutAsync() on the same chatForm instance; see Sign-out and shared PCs below.

What the sample does for you

Only your wrapper page loads in the app. The address must match ChatUrl exactly: HTTPS only, the scheme and host in any letter case, the same port (443 when none is written), and the same path and query including letter case. An address with a user name or password in it is refused. Any other top-level address is cancelled. Ordinary http and https links open in the default browser; javascript:, file:, data: and custom schemes are blocked. raia stays inside its iframe on your page.

vb
Private Sub OnNavigationStarting(sender As Object, e As CoreWebView2NavigationStartingEventArgs)
    ' Top-level document only. Frame navigations (the raia iframe) do not raise this event.
    If IsWrapperUrl(e.Uri) Then
        ' The wrapper is really (re)loading: its helpers are gone until NavigationCompleted checks again.
        hostReady = False
        wrapperNavigationId = e.NavigationId
        Return
    End If
    ' Cancelled, so the wrapper page that is already loaded stays loaded and usable.
    e.Cancel = True
    OpenExternally(e.Uri)
End Sub

When a link is cancelled, the chat stays ready, because the wrapper page never left. A cancelled navigation still raises NavigationCompleted as a failure, so the sample only reacts to the completion of the wrapper load it allowed.

Each chat window gets its own private browser session. The sample opens every chat window in a new InPrivate WebView2 profile with a new profile name, so no saved browser profile is shared between people who use the same Windows account. If the installed WebView2 Runtime cannot create an InPrivate profile, the chat shows a message and does not open, rather than falling back to a shared profile.

It waits for the page, not for a fixed time. After NavigationCompleted succeeds for the wrapper load, it checks that window.raiaHost exists before sending anything. If navigation fails or the helper is missing, it shows a short message instead of a blank window, and it does not retry in a loop.

It sends values safely. Every value is JSON-encoded before it becomes JavaScript, so names such as O'Brien, quotes, backslashes and line breaks cannot break the call. Script runs only when the current page is your wrapper.

vb
Private Function CallHostAsync(method As String, jsonArgs As String) As Task
    Dim core = chatView.CoreWebView2
    If core Is Nothing OrElse Not hostReady OrElse Not IsWrapperUrl(core.Source) Then
        Throw New InvalidOperationException("Chat is not ready. The command was not sent.")
    End If
    Return core.ExecuteScriptAsync("window.raiaHost." & method & "(" & jsonArgs & ");")
End Function

ExecuteScriptAsync finishes when that line of script has run. It does not wait for the helper's promise or for raia to accept the command. Listen for the ack and error events instead.

It checks every message from the page. WebMessageReceived ignores messages unless they come from the exact wrapper address, parse as JSON, use the expected fields and name one of the known events.

It denies browser permissions. Every permission request, including the microphone, is denied. Treat voice as a separate opt-in: allow the microphone only for the exact raia origin, and only after you have tested voice on your platform.

Read Microsoft's WebView2 overview and WebView2 security guidance before you change these rules.

Step 4: Add the chat to a PxPlus app

A PxPlus developer does this step. The ZIP includes pxplus/RAIACHAT-illustrative.txt. It is an illustrative excerpt, not a tested program, and syntax differs between versions. Check each line against the PxPlus *BROWSER manual.

  1. Set CHAT_URL$ to the same HTTPS wrapper address.
  2. Create the *BROWSER object and call Navigate with that address.
  3. Check for window.raiaHost with ExecJS in a bounded loop, for example 50 tries 0.2 seconds apart. ExecJS runs the script in the loaded page and returns its result, so the loop can read 1 or 0. If it never appears, show a friendly message and delete the object.
  4. Pass each value through the excerpt's FN_JS$ function, which escapes it as a JavaScript string, then call window.raiaHost.setUser(...) with ExecJS.
  5. Run your normal event loop. When the chat closes, use DELETE OBJECT.

Before real use, a developer must add three things the excerpt leaves out, checked against the manual for your PxPlus version: your application's own event loop, handling for the navigation and new-window events you permit, and sign-out cleanup for shared PCs (see Sign-out and shared PCs).

text
LET READY=0
FOR I=1 TO 50
 LET READY=RAIA'EXECJS("typeof window.raiaHost==='object'?1:0")
 IF READY=1 THEN BREAK
 WAIT .2
NEXT I
IF READY<>1 THEN GOTO NOT_READY

ExecJS does not by itself wait for raia to acknowledge an asynchronous command such as setUser, so the value it returns is not a raia acknowledgement. Confirm in the chat itself that the agent uses the name you passed.

*BROWSER runs in incognito mode by default (system parameter 'BI' is On), and the manual says an incognito browser does not store or cache anything. Keeping history means a later user of the same Windows account may see an earlier chat, so decide on purpose. If you change 'BI', do it before the first browser object is created. With 'BI' Off, closing and recreating the browser object does not clear stored chat state: confirm that it is cleared, or give each user a fresh isolated profile.

Do not assume every link in the chat opens outside the app. Test agent replies with links and popups, and handle the navigation and new-window events your PxPlus version documents in the manual. Before PxPlus 2026 Update 1, a BeforeNavigate or BeforeNavigate2 handler must not call ExecJS or use the Document, Location, Window or related properties; the manual warns that the system will hang.

Sign-out and shared PCs

When a user signs out of your app, clear the chat before the next person uses it.

  • WinForms: call SignOutAsync() and wait for it. It asks the page to clear the user, then the context, then reset the conversation, each only after the previous one is accepted. Then it always closes the chat window, so the user's details cannot be sent again if the page reloads. It returns True only when signout-complete arrived within 10 seconds. False means the reset was not confirmed: the sample tells the user so and never reports it as cleared. Because each chat window used its own InPrivate profile, the next user's chat window does not reuse it. While sign-out runs, the sample disables its Support chat and Sign out buttons. If the user already closed the chat window, there is nothing to reset.
  • PxPlus: call window.raiaHost.signOut(...) and then delete the browser object. ExecJS does not wait for that asynchronous sign-out to finish, so do not reuse the browser object for the next user. If you turned 'BI' Off, closing the object is not enough; see Step 4.

Closing a window does not by itself delete a saved browser profile. That is why the WinForms sample uses a private profile for each window instead of clearing a shared one.

Sign-out resets chat state in this app. It does not delete conversation history stored by raia and does not sign anyone in or out of raia.

Test checklist

Use a fictional test identity such as Testy O'Brien, never real customer data.

#TestExpected result
1Open the wrapper address in Edge or Chrome and ask a question.A real agent reply appears.
2Open the chat in the desktop app and ask the same question.A real agent reply appears in the app window.
3Pass the fictional user, then ask "What's my name?"The reply uses the name. In WinForms, the log also shows ack SET_USER; an ack alone is not a reply. PxPlus has no event log, so check the reply.
4Use a name with an apostrophe, a quote, a backslash and a line break.The call still works and nothing breaks.
5Disconnect the network, or point ChatUrl at a wrong address.A friendly message appears; the app does not crash.
6Try to navigate the app to another site or a javascript: or file: link.It does not load inside the app.
7Click a link and a popup in an agent reply.It opens in the default browser, or is blocked, as you intended, and the chat in the app still works.
8Sign out, sign in as a second fictional user and open the chat.No name, context or conversation from the first user appears.
9Open the wrapper from an origin that is not allowed (if you set origins).The chat does not start there.
10Have a conversation, close the chat window or the app, then open the chat again.WinForms: the earlier conversation does not appear, because each chat window uses its own private profile. PxPlus: with 'BI' On, the earlier conversation does not appear; with 'BI' Off, it matches the history choice and cleanup you made.

Troubleshooting

SymptomLikely cause and fix
Blank window or "needs the WebView2 Runtime"The runtime is missing or the data folder is not writable. Install the runtime and use a per-user folder.
"Chat needs a newer Microsoft Edge WebView2 Runtime"The installed runtime cannot create the private profile the sample requires. Update the runtime; do not remove the private profile to make it start.
"Chat is unavailable right now"The raia iframe script did not load, or window.raiaHost is missing. Check that the wrapper address is correct and reachable.
Works in a browser but not in the appThe app loads a different address than the one you tested or allowed. Use the exact same HTTPS address.
Chat never starts, or the WinForms log shows error INITA security key is on but missing or wrong in the wrapper. Copy it again from Live Chat → Security.
The chat does not start, even in a normal browserThe agent is not Public, or the Live Chat skill is not enabled. Check Step 1.
setUser seems to do nothingIt was sent before the page was ready, or it was rejected. In WinForms, check the log for ack SET_USER or error SET_USER. In PxPlus, check that the readiness loop succeeded before the call.
A CAPTCHA or verification appearsComplete it as a normal user in an approved browser or app. Do not try to bypass it.
PxPlus chat history disappears every launch*BROWSER is incognito by default. Change 'BI' only if you have decided to keep history.
WinForms log shows ack but no answerThe command was accepted, but the agent has not replied. Check the agent's setup and test the same question in a normal browser.

Before rollout: This starter has not been verified end to end in WinForms or PxPlus. Complete the checklist on your supported PCs before rollout.