@primexperts.co/pulse-agent 5.8.2 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,24 @@
1
1
  # @primexperts.co/pulse-agent
2
2
 
3
- Pulse Agent is a browser-only customer support widget for adding Pulse Webchat and optional website error reporting to any public website, SPA, or client-rendered application.
3
+ Pulse Agent adds a website chat channel to Pulse. Visitors send enquiries, booking requests, support requests, or other configured request types from your site. Your team handles them in the shared Pulse workspace.
4
4
 
5
- It is designed for teams that want one lightweight install to do two jobs:
5
+ The package runs in the browser and works with Angular, React, Vue, static sites, and other client-rendered applications.
6
6
 
7
- - Add a floating Pulse chat launcher to a website.
8
- - Report browser errors and failed API calls back to Pulse when error monitoring is enabled.
9
-
10
- The package is framework-agnostic. It works with Angular, React, Vue, static HTML, and SSR frameworks when initialized on the client side.
11
-
12
- ## Quick Start
7
+ ## Install
13
8
 
14
9
  ```bash
15
10
  npm install @primexperts.co/pulse-agent@latest
16
11
  ```
17
12
 
13
+ ## Set up in Pulse
14
+
15
+ 1. Create an organization and an application for your website.
16
+ 2. Enable Webchat for the application.
17
+ 3. Copy its public application key and your organization's slug.
18
+ 4. Configure the Pulse API to allow requests from your website origin.
19
+
20
+ ## Use
21
+
18
22
  ```ts
19
23
  import { createPulseAgent } from '@primexperts.co/pulse-agent';
20
24
 
@@ -24,460 +28,33 @@ const agent = createPulseAgent({
24
28
  apiBaseUrl: 'https://pulse.service.primexperts.co.za',
25
29
  title: 'Support',
26
30
  requireProfile: true,
27
- visitorProfile: { name: 'Ada Lovelace', email: 'ada@example.com' },
28
- autoOpen: false,
29
- errorMonitoring: true,
30
- captureUnhandledErrors: true,
31
- captureUnhandledRejections: true,
32
- captureFetchErrors: true,
33
- environment: 'production',
34
- release: 'web-1.0.0'
31
+ autoOpen: false
35
32
  });
36
33
 
37
34
  agent.open();
38
35
  ```
39
36
 
40
- ## Why Teams Use Pulse Agent
41
-
42
- Pulse Agent is useful when a business has customer messages arriving through public-facing channels, but the team needs a controlled place to handle them.
43
-
44
- Typical use cases:
45
-
46
- - A website visitor starts a chat and any available team member can respond from Pulse.
47
- - A support mailbox can be connected so the team manages customer email from a shared workspace instead of one person's inbox.
48
- - Conversation history stays attached to the customer interaction, so the next team member can continue with context.
49
- - Teams can separate customer communication by organization/application using the `slug` and `appKey`.
50
- - Website errors can be reported into Pulse so support and operations can see when customers are affected.
51
-
52
- The result is a team inbox model: one public channel for customers, multiple internal users for response and follow-up.
53
-
54
- ## Channel Model
55
-
56
- | Channel | Current role | What the customer sees | What the team gets in Pulse |
57
- | --- | --- | --- | --- |
58
- | Webchat | Supported through this package | Website chat launcher | Shared conversation thread, visitor profile, realtime replies, history, transcript action |
59
- | Email | Supported by Pulse integrations | Normal support email address | Shared email conversations in Pulse, independent of one person's mailbox |
60
- | WhatsApp | Planned/optional depending on deployment | WhatsApp contact path | Intended shared handling model when enabled; do not promise as active unless configured |
61
- | Error Monitoring | Supported through this package | No visible customer UI | Browser exceptions and failed request reports tied to the website/application |
62
-
63
- ## Self-Service Setup
64
-
65
- Pulse Agent is intended to be usable by any website owner who creates a Pulse workspace and enables Webchat.
66
-
67
- Before adding the code snippet, do this in Pulse:
68
-
69
- 1. Create or sign in to your Pulse account.
70
- 2. Create your organization or workspace.
71
- 3. Create/select the application or website you want to connect.
72
- 4. Enable the Webchat integration.
73
- 5. Copy the generated setup values into your website environment/config.
74
- 6. Add your website origin, for example `https://www.example.com`, to the allowed origins/CORS settings if your Pulse deployment exposes that setting.
75
-
76
- Use these values in your widget config:
77
-
78
- | Value | Example | Required | How to get it |
79
- | --- | --- | --- | --- |
80
- | Organization slug | `acme-support` | Yes | Shown in Pulse for your organization/workspace. |
81
- | Application key | `app_...` | Recommended when available | Copy from your Pulse application/webchat setup. |
82
- | API base URL | `https://pulse.service.primexperts.co.za` | Yes | Use the API URL shown by your Pulse environment. |
83
- | Website origin | `https://www.example.com` | Required for production | Add the exact site origin where the widget will run. |
84
-
85
- You usually do not need a widget token. Pulse Agent resolves it automatically from your organization slug; when an application key is configured, the agent also sends it for app-aware deployments and reporting context.
86
-
87
- If your website already signs the user in, pass the authenticated user profile explicitly through `visitorProfile` or later via `agent.update({ visitorProfile: ... })`. The widget does not scrape browser pages, cookies, or DOM state to discover names or email addresses.
88
- ## Configuration Keys
89
-
90
- | Option | Type | Default | Description |
91
- | --- | --- | --- | --- |
92
- | `slug` | `string` | Required | Public organization slug used to resolve the active webchat widget. |
93
- | `appKey` | `string` | Optional but recommended | Public application key, normally starts with `app_`. Use this for application-specific setup and error attribution. |
94
- | `appSlug` | `string` | Optional | Alternative application identifier when an app key is not available. |
95
- | `apiBaseUrl` | `string` | `https://pulse.service.primexperts.co.za` | Pulse API host only. Do not include `/api/...` paths. |
96
- | `title` | `string` | Organization/app name | Chat panel title. |
97
- | `mount` | `HTMLElement \| string` | `document.body` | Element or selector where the widget root should be mounted. |
98
- | `autoOpen` | `boolean` | `false` | Opens the chat panel immediately after initialization. |
99
- | `requireProfile` | `boolean` | `true` | Requires visitor name/email before first message. |
100
- | `visitorProfile` | `{ name: string; email: string } \| null` | Optional | Host-supplied signed-in user profile. Prefills the gate and hides it when provided. |
101
- | `followUpEmailTo` | `string` | Optional | Destination used by transcript/follow-up email actions. |
102
- | `dashboardUrl` | `string` | `https://pulse.primexperts.co.za` | Dashboard link used in setup/error states. |
103
- | `dashboardSetupPath` | `string` | `/integrations` | Dashboard path linked when setup is incomplete. |
104
- | `whatsappUrl` | `string` | Optional | Adds a WhatsApp launcher alongside Pulse Chat. |
105
- | `widgetToken` | `string` | Optional | Advanced bypass for resolve flow. Leave unset unless Pulse gives you a specific token. |
106
- | `errorMonitoring` | `boolean` | `false` | Enables browser error reporting. Must be `true` before any capture happens. |
107
- | `captureUnhandledErrors` | `boolean` | `true` when monitoring is enabled | Captures `window.onerror` events. |
108
- | `captureUnhandledRejections` | `boolean` | `true` when monitoring is enabled | Captures unhandled promise rejections. |
109
- | `captureFetchErrors` | `boolean` | `true` when monitoring is enabled | Wraps browser `fetch` to report failed requests and thrown network errors. |
110
- | `environment` | `string` | Optional | Environment label such as `production`, `staging`, or `development`. |
111
- | `release` | `string` | Optional | Application release/version label for error context. |
112
-
113
- ## Environment Variables
114
-
115
- Pulse Agent itself receives JavaScript options. For deployed apps, put values in your platform environment and generate or serve a runtime config file that passes those values to `createPulseAgent`.
116
-
117
- Recommended names:
118
-
119
- ```env
120
- PULSE_AGENT_SLUG=your-organization-slug
121
- PULSE_AGENT_APP_KEY=app_your_public_application_key
122
- PULSE_AGENT_API_BASE_URL=https://pulse.service.primexperts.co.za
123
- PULSE_AGENT_TITLE=Support
124
- PULSE_AGENT_ENVIRONMENT=production
125
- PULSE_AGENT_RELEASE=web-1.0.0
126
-
127
- PULSE_AGENT_ERROR_MONITORING=true
128
- PULSE_AGENT_CAPTURE_UNHANDLED_ERRORS=true
129
- PULSE_AGENT_CAPTURE_UNHANDLED_REJECTIONS=true
130
- PULSE_AGENT_CAPTURE_FETCH_ERRORS=true
131
- ```
132
-
133
- Optional names:
134
-
135
- ```env
136
- PULSE_AGENT_FOLLOW_UP_EMAIL_TO=support@example.com
137
- PULSE_AGENT_DASHBOARD_URL=https://pulse.primexperts.co.za
138
- PULSE_AGENT_DASHBOARD_SETUP_PATH=/integrations
139
- PULSE_AGENT_AUTO_OPEN=false
140
- PULSE_AGENT_REQUIRE_PROFILE=true
141
- PULSE_AGENT_WHATSAPP_URL=https://api.whatsapp.com/send?phone=27123456789
142
- PULSE_AGENT_WIDGET_TOKEN=
143
- ```
144
-
145
- Leave `PULSE_AGENT_WIDGET_TOKEN` unset unless Pulse Support explicitly gives you one. It is not the `app_*` key.
146
-
147
- ## Deployment Environment Variables
148
-
149
- Set the same values in whatever platform you deploy on. The host should provide them as environment variables during build or at runtime, and your app should expose them to the browser through a runtime config file or server-rendered bootstrap.
150
-
151
- ```env
152
- PULSE_AGENT_SLUG=your-organization-slug
153
- PULSE_AGENT_APP_KEY=app_your_public_application_key
154
- PULSE_AGENT_API_BASE_URL=https://pulse.service.primexperts.co.za
155
- PULSE_AGENT_TITLE=Support
156
- PULSE_AGENT_ENVIRONMENT=production
157
- PULSE_AGENT_ERROR_MONITORING=true
158
- PULSE_AGENT_CAPTURE_UNHANDLED_ERRORS=true
159
- PULSE_AGENT_CAPTURE_UNHANDLED_REJECTIONS=true
160
- PULSE_AGENT_CAPTURE_FETCH_ERRORS=true
161
- ```
162
-
163
- If your app uses a `runtime-config.js` file, generate that file from environment values at request time, build time, or startup depending on how your host serves assets.
164
-
165
- ## Required Code Structure
166
-
167
- Pulse Agent does not require a special framework architecture, but it does need one clear bootstrap point.
168
-
169
- Structure your app so `createPulseAgent(...)` runs:
170
-
171
- - once per page/app session;
172
- - only in the browser, never during server-side rendering;
173
- - as early as practical after runtime config is available;
174
- - before the user flows you want error monitoring to observe;
175
- - from a stable root entry point such as `main.ts`, `App.tsx`, a root layout client component, `onMounted`, or a script loaded after `/runtime-config.js`.
176
-
177
- Do not initialize Pulse Agent inside frequently re-rendered child components, route components that mount repeatedly, button click handlers, or server-only files. If the root component can unmount, keep the returned handle and call `agent.destroy()` during cleanup.
178
-
179
- For error monitoring, timing matters: errors that happen before `createPulseAgent(...)` runs cannot be captured by Pulse Agent. If you want broad coverage, load runtime config first and initialize Pulse Agent before bootstrapping the rest of the application where your framework allows it.
180
- ## Runtime Config Pattern
181
-
182
- A common production-safe pattern is to load `/runtime-config.js` before your app bundle:
183
-
184
- ```html
185
- <script src="/runtime-config.js"></script>
186
- ```
187
-
188
- Example generated file:
189
-
190
- ```js
191
- window.__APP_RUNTIME_CONFIG__ = {
192
- pulseAgent: {
193
- slug: 'your-organization-slug',
194
- appKey: 'app_your_public_application_key',
195
- apiBaseUrl: 'https://pulse.service.primexperts.co.za',
196
- title: 'Support',
197
- errorMonitoring: true,
198
- captureUnhandledErrors: true,
199
- captureUnhandledRejections: true,
200
- captureFetchErrors: true,
201
- environment: 'production'
202
- }
203
- };
204
- ```
205
-
206
- Then initialize in your app entry point:
37
+ Pass a profile only when the visitor has provided it or signed in:
207
38
 
208
39
  ```ts
209
- import { createPulseAgent, type PulseAgentOptions } from '@primexperts.co/pulse-agent';
210
-
211
- declare global {
212
- interface Window {
213
- __APP_RUNTIME_CONFIG__?: {
214
- pulseAgent?: Partial<PulseAgentOptions>;
215
- };
216
- }
217
- }
218
-
219
- const config = window.__APP_RUNTIME_CONFIG__?.pulseAgent;
220
-
221
- if (config?.slug) {
222
- createPulseAgent({
223
- ...config,
224
- slug: config.slug
225
- });
226
- }
227
- ```
228
-
229
- ## Browser/CDN Usage
230
-
231
- Use this when you cannot install npm packages:
232
-
233
- ```html
234
- <script src="https://unpkg.com/@primexperts.co/pulse-agent@latest/dist/browser/pulse-agent.global.js"></script>
235
- <script>
236
- window.PulseAgent.createPulseAgent({
237
- slug: 'your-organization-slug',
238
- appKey: 'app_your_public_application_key',
239
- apiBaseUrl: 'https://pulse.service.primexperts.co.za',
240
- title: 'Support',
241
- errorMonitoring: true
242
- });
243
- </script>
244
- ```
245
-
246
- ## Framework Examples
247
-
248
- ### Angular
249
-
250
- Initialize in `main.ts` or a browser-only root component. For SSR, guard against server execution.
251
-
252
- ```ts
253
- import { bootstrapApplication } from '@angular/platform-browser';
254
- import { createPulseAgent } from '@primexperts.co/pulse-agent';
255
- import { AppComponent } from './app/app.component';
256
-
257
- if (typeof window !== 'undefined') {
258
- createPulseAgent({
259
- slug: 'your-organization-slug',
260
- appKey: 'app_your_public_application_key',
261
- apiBaseUrl: 'https://pulse.service.primexperts.co.za',
262
- errorMonitoring: true
263
- });
264
- }
265
-
266
- bootstrapApplication(AppComponent);
40
+ agent.identify({ name: 'Ada Lovelace', email: 'ada@example.com' });
267
41
  ```
268
42
 
269
- ### React
270
-
271
- ```tsx
272
- import { useEffect } from 'react';
273
- import { createPulseAgent } from '@primexperts.co/pulse-agent';
274
-
275
- export function PulseAgentBootstrap() {
276
- useEffect(() => {
277
- const agent = createPulseAgent({
278
- slug: 'your-organization-slug',
279
- appKey: 'app_your_public_application_key',
280
- apiBaseUrl: 'https://pulse.service.primexperts.co.za',
281
- errorMonitoring: true
282
- });
283
-
284
- return () => agent.destroy();
285
- }, []);
286
-
287
- return null;
288
- }
289
- ```
290
-
291
- ### Vue
292
-
293
- ```ts
294
- import { onBeforeUnmount, onMounted } from 'vue';
295
- import { createPulseAgent, type PulseAgentHandle } from '@primexperts.co/pulse-agent';
296
-
297
- let agent: PulseAgentHandle | null = null;
298
-
299
- onMounted(() => {
300
- agent = createPulseAgent({
301
- slug: 'your-organization-slug',
302
- appKey: 'app_your_public_application_key',
303
- apiBaseUrl: 'https://pulse.service.primexperts.co.za',
304
- errorMonitoring: true
305
- });
306
- });
307
-
308
- onBeforeUnmount(() => agent?.destroy());
309
- ```
310
-
311
- ## How Widget Resolution Works
312
-
313
- At startup or first send, Pulse Agent calls:
314
-
315
- ```text
316
- GET /api/pulse/public/webchat/resolve?slug=<slug>[&appKey=<app_...>]
317
- ```
318
-
319
- Pulse returns the active widget configuration, including an internal `widgetToken`. The widget then uses that token for chat, history, streaming, transcript email, and error reporting.
320
-
321
- Do not confuse these values:
322
-
323
- | Name | Format | Purpose | User action |
324
- | --- | --- | --- | --- |
325
- | `appKey` | `app_...` | Public application key from Pulse Dashboard | Put in config/env. |
326
- | `widgetToken` | Token generated by Pulse | Internal public webchat token used by endpoints | Usually leave unset. |
327
- | `slug` | human-readable slug | Identifies organization | Put in config/env. |
328
-
329
- ## Network Access and Security Scanners
330
-
331
- Security scanners such as Socket may flag this package for network access. That is expected and intentional.
332
-
333
- Pulse Agent is a browser widget, so it uses standard browser networking APIs to provide its features:
334
-
335
- - `fetch` resolves the active widget configuration.
336
- - `fetch` sends visitor messages to Pulse.
337
- - `fetch` loads conversation history and sends transcript/error-report requests.
338
- - `EventSource` keeps the chat connected for realtime replies.
339
- - Optional `fetch` wrapping reports failed website requests only when `errorMonitoring` and `captureFetchErrors` are enabled.
340
-
341
- The package does not run install scripts, does not start background processes, does not open arbitrary sockets, and does not use Node filesystem or shell APIs at runtime. Network calls are made from the visitor's browser after the website initializes `createPulseAgent(...)`, and they target the configured `apiBaseUrl` plus optional WhatsApp links when configured.
342
- ## Error Monitoring
343
-
344
- Error monitoring is opt-in. Installing the package does not capture anything until `errorMonitoring: true` is passed to `createPulseAgent`.
345
-
346
- When enabled, Pulse Agent can capture:
347
-
348
- - Unhandled JavaScript errors from `window.onerror`.
349
- - Unhandled promise rejections from `window.onunhandledrejection`.
350
- - Failed `fetch` responses, including HTTP statuses from failed API calls.
351
- - Network errors thrown by `fetch`.
352
- - Manual error reports through `agent.captureException(...)`.
353
-
354
- Manual capture example:
355
-
356
- ```ts
357
- try {
358
- await saveOrder();
359
- } catch (error) {
360
- agent.captureException(error, {
361
- source: 'checkout.saveOrder',
362
- errorType: 'checkout-error',
363
- endpoint: '/api/orders',
364
- pageUrl: window.location.href
365
- });
366
- throw error;
367
- }
368
- ```
369
-
370
- Report count example:
371
-
372
- ```ts
373
- const reportsLastSevenDays = agent.getErrorReportCount(7);
374
- ```
375
-
376
- ### What Error Monitoring Does Not Do
377
-
378
- Pulse Agent does not:
379
-
380
- - Catch errors before the widget is initialized.
381
- - Catch server-side errors unless those errors are exposed to the browser as failed requests.
382
- - Replace backend logging, APM, or infrastructure monitoring.
383
- - Read private app state, passwords, tokens, or request bodies intentionally.
384
- - Automatically fix errors.
385
- - Capture errors if `errorMonitoring` is missing or `false`.
386
-
387
- ## Public Backend Endpoints
388
-
389
- The widget expects these Pulse endpoints:
390
-
391
- ```text
392
- GET /api/pulse/public/webchat/resolve?slug=<slug>[&appKey=<appKey>]
393
- POST /api/pulse/public/webchat/{widgetToken}/message
394
- GET /api/pulse/public/webchat/{widgetToken}/messages?conversationId=<id>
395
- POST /api/pulse/public/webchat/{widgetToken}/transcript-email?conversationId=<id>
396
- GET /api/pulse/public/webchat/{widgetToken}/stream?conversationId=<id>
397
- POST /api/pulse/public/webchat/{widgetToken}/errors
398
- ```
399
-
400
- Admin setup endpoints are separate and should remain authenticated.
401
-
402
- ## Benefits
403
-
404
- - One package for webchat and optional browser error monitoring.
405
- - Framework-agnostic browser integration.
406
- - Works with npm or a script tag.
407
- - Supports runtime configuration for static hosting, server-rendered apps, containerized apps, and other multi-environment deployments.
408
- - Uses the organization `slug`, plus `appKey` when available, so users do not need to handle internal widget tokens.
409
- - Supports visitor profile capture, history loading, transcript email requests, SSE replies, WhatsApp launcher/contact path where configured, manual open/close, and manual exception reporting.
410
- - Supports operational workflows verified in the Pulse source: assignment, status/priority updates, internal notes, follow-ups, customer profiles, tags, notifications, activity logs, exports, and dashboard KPIs.
411
- - Supports role-based team operation: admins manage setup and assignment, while agents work assigned conversations.
412
-
413
- ## Limitations
414
-
415
- - Browser-only. Initialize after mount/hydration in SSR frameworks.
416
- - Requires the website origin to be allowed by Pulse CORS configuration.
417
- - Requires an active Pulse Webchat integration for the organization/application.
418
- - Requires `fetch` and `EventSource` support for full behavior.
419
- - SSE can be affected by proxies, corporate networks, browser extensions, and load balancer timeouts.
420
- - Error monitoring starts only after initialization and only when enabled.
421
- - `fetch` monitoring covers `window.fetch`; it does not automatically wrap Axios internals unless Axios uses `fetch` in that environment.
422
- - `widgetToken` is an advanced bypass and should not be used unless Pulse explicitly provides it.
423
-
424
- ## Production Checklist
425
-
426
- Before publishing a site with Pulse Agent:
427
-
428
- 1. Confirm Pulse Webchat is enabled in the Pulse Dashboard.
429
- 2. Confirm you have the correct `slug`.
430
- 3. Confirm you have the correct `appKey` beginning with `app_`.
431
- 4. Confirm `apiBaseUrl` is `https://pulse.service.primexperts.co.za`.
432
- 5. Confirm production origin is allowed by Pulse CORS configuration.
433
- 6. Confirm `runtime-config.js` or app config is generated from deployment env values.
434
- 7. Confirm `errorMonitoring` is `true` if you expect browser error reporting.
435
- 8. Open the site and check browser network calls for `resolve`, `message`, `stream`, and `errors` when applicable.
436
- 9. Send a test chat message.
437
- 10. Trigger a test error in a non-production or controlled environment and confirm it appears in Pulse.
438
-
439
- ## Handle API
440
-
441
- `createPulseAgent(options)` returns:
442
-
443
- | Method | Description |
444
- | --- | --- |
445
- | `open()` | Opens the chat panel. |
446
- | `close()` | Closes the chat panel. |
447
- | `destroy()` | Removes DOM, listeners, and fetch wrapping installed by this instance. |
448
- | `resetConversation()` | Starts a fresh local conversation. |
449
- | `update(partialOptions)` | Updates runtime options such as title, WhatsApp URL, or widget token. |
450
- | `captureException(error, context)` | Manually reports an error when monitoring is enabled. |
451
- | `getErrorReportCount(days)` | Returns locally remembered successful error report count for the last N days. |
452
-
453
- ## Troubleshooting
454
-
455
- ### The widget does not appear
456
-
457
- Check that initialization runs in the browser, the `slug` is present, and the mount selector exists.
458
-
459
- ### `Failed to resolve widget keys (401/403)`
460
-
461
- The public resolve endpoint is protected or the key is not accepted. Confirm the Pulse public webchat routes are accessible and the `appKey` is correct.
462
-
463
- ### `Failed to resolve widget keys (404)`
464
-
465
- The slug is wrong, Webchat is not enabled, or the selected organization/application has no active widget.
466
-
467
- ### CORS error in the browser console
43
+ Pulse Agent resolves the Webchat widget token from the public configuration endpoint. A website normally needs the organization slug and public application key only.
468
44
 
469
- The website origin is missing from Pulse CORS configuration. Add the exact origin, including protocol and port.
45
+ ## Public API
470
46
 
471
- ### Chat sends but replies do not stream
47
+ `createPulseAgent(options)` returns a handle with `open()`, `close()`, `destroy()`, `resetConversation()`, `identify(profile)`, and `update(options)`.
472
48
 
473
- Check that `EventSource` is available and that proxies/load balancers allow long-lived SSE connections.
49
+ The widget uses `fetch` to resolve configuration, send messages, and load conversation history. It uses `EventSource` for live replies. It does not install global error handlers or wrap the host website's `fetch`.
474
50
 
475
- ### Errors are not captured
51
+ ## Privacy
476
52
 
477
- Confirm all of the following:
53
+ Collect only the details needed to respond. Provide a privacy notice URL with `privacyPolicyUrl` if you want the widget to link to your own notice. Review data retention and team access in Pulse before going live.
478
54
 
479
- - `errorMonitoring: true` is present in runtime config.
480
- - The widget successfully resolves a widget token.
481
- - The error happens after `createPulseAgent(...)` runs.
482
- - The browser can post to `/api/pulse/public/webchat/{widgetToken}/errors`.
55
+ ## Verify
483
56
 
57
+ 1. Open the website as a visitor and send a test request.
58
+ 2. Confirm that it appears in the correct Pulse Inbox.
59
+ 3. Reply from Pulse and confirm the visitor receives the response.
60
+ 4. Check the privacy notice link.