@brandfine/client 0.11.0 → 0.14.0
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/CHANGELOG.md +37 -0
- package/README.md +22 -12
- package/dist/index.cjs +473 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +242 -12
- package/dist/index.d.ts +242 -12
- package/dist/index.js +473 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -3,6 +3,110 @@ export { e as BrandfineNavItem, f as BrandfineNavItemType, g as BrandfineNavPost
|
|
|
3
3
|
export { Cache, CacheOptions, KeyedCache, KeyedCacheOptions, createCache, createKeyedCache } from './cache/index.cjs';
|
|
4
4
|
export { BrandfineWebhookEvent, BrandfineWebhookHandlerOptions, BrandfineWebhookPayload, createBrandfineWebhookHandler, parseWebhookPayload, verifyWebhookSecret } from './webhook/index.cjs';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Headless Live Chat session — the conversation WITHOUT the widget.
|
|
8
|
+
*
|
|
9
|
+
* `bf.liveChat.createSession()` gives a consumer everything needed to
|
|
10
|
+
* render chat inline in their own design system: transcript state, a
|
|
11
|
+
* send method, and a subscribe API shaped for
|
|
12
|
+
* `useSyncExternalStore` / Svelte stores / Vue refs. Brandfine keeps
|
|
13
|
+
* owning transport, identity, threading and storage; the consumer
|
|
14
|
+
* owns pixels.
|
|
15
|
+
*
|
|
16
|
+
* Storage keys are IDENTICAL to the floating widget's, so a visitor
|
|
17
|
+
* who talks through the widget on one page and an inline panel on
|
|
18
|
+
* another continues the same thread.
|
|
19
|
+
*/
|
|
20
|
+
type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM';
|
|
21
|
+
type ChatMessage = {
|
|
22
|
+
id: string;
|
|
23
|
+
body: string;
|
|
24
|
+
sender: ChatMessageSender;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
/** Monotonic per-conversation sequence — the realtime resume
|
|
27
|
+
* cursor. Optional: older APIs don't send it. */
|
|
28
|
+
eventId?: number;
|
|
29
|
+
};
|
|
30
|
+
type ConversationStatus = 'OPEN' | 'CLOSED';
|
|
31
|
+
type LiveChatSessionState = 'CONNECTING' | 'OPEN' | 'CLOSED' | 'ERROR';
|
|
32
|
+
/** The `/external/live-chat/config` shape — the RUNTIME config the
|
|
33
|
+
* widget fetches per page load. Distinct from the server-side
|
|
34
|
+
* `LiveChatBootstrap` (which additionally carries the publishable
|
|
35
|
+
* key + script path). */
|
|
36
|
+
type LiveChatRuntimeConfig = {
|
|
37
|
+
enabled: false;
|
|
38
|
+
} | {
|
|
39
|
+
enabled: true;
|
|
40
|
+
greeting: string | null;
|
|
41
|
+
offlineMessage: string | null;
|
|
42
|
+
theme: Record<string, string> | null;
|
|
43
|
+
online?: boolean;
|
|
44
|
+
/** Realtime endpoint (wss://…). Absent = poll (older API, or
|
|
45
|
+
* the transport is off). The session upgrades automatically
|
|
46
|
+
* when present; consumers never touch it. */
|
|
47
|
+
realtimeUrl?: string;
|
|
48
|
+
};
|
|
49
|
+
type LiveChatSessionSnapshot = {
|
|
50
|
+
messages: ChatMessage[];
|
|
51
|
+
status: LiveChatSessionState;
|
|
52
|
+
/** Agents available right now (business-hours based today; agent
|
|
53
|
+
* presence once the realtime transport lands). */
|
|
54
|
+
online: boolean;
|
|
55
|
+
/** Localized via the session's `locale`. */
|
|
56
|
+
greeting: string | null;
|
|
57
|
+
offlineMessage: string | null;
|
|
58
|
+
/** An outbound `send()` is in flight. */
|
|
59
|
+
sending: boolean;
|
|
60
|
+
error: Error | null;
|
|
61
|
+
};
|
|
62
|
+
type StartConversationResult = {
|
|
63
|
+
enabled: false;
|
|
64
|
+
} | {
|
|
65
|
+
enabled: true;
|
|
66
|
+
conversationId: string;
|
|
67
|
+
conversationToken: string;
|
|
68
|
+
greeting: string | null;
|
|
69
|
+
resumed: boolean;
|
|
70
|
+
online?: boolean;
|
|
71
|
+
};
|
|
72
|
+
type LiveChatCreateSessionOptions = {
|
|
73
|
+
/** The server-half bootstrap (same object `install()` takes) —
|
|
74
|
+
* supplies the publishable key (and, when the API advertises it,
|
|
75
|
+
* the realtime endpoint — passed through wholesale). */
|
|
76
|
+
config: {
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
publishableKey?: string;
|
|
79
|
+
realtimeUrl?: string;
|
|
80
|
+
};
|
|
81
|
+
/** Signed identity — SAME shape and rules as `install()`. A bad
|
|
82
|
+
* signature downgrades to anonymous server-side; it is never a
|
|
83
|
+
* second identity path. */
|
|
84
|
+
visitor?: Record<string, unknown>;
|
|
85
|
+
/** BCP-47 tag for greeting/away localization. Defaults to
|
|
86
|
+
* `<html lang>` then browser language, like the widget. */
|
|
87
|
+
locale?: string;
|
|
88
|
+
/** Defaults to `location.href`. */
|
|
89
|
+
pageUrl?: string;
|
|
90
|
+
referrer?: string;
|
|
91
|
+
/** Transcript refresh cadence. Polling is the transport today; a
|
|
92
|
+
* realtime upgrade will keep this as the fallback. */
|
|
93
|
+
pollIntervalMs?: number;
|
|
94
|
+
};
|
|
95
|
+
type LiveChatSession = {
|
|
96
|
+
/** Current transcript (same array identity as the latest snapshot). */
|
|
97
|
+
readonly messages: readonly ChatMessage[];
|
|
98
|
+
readonly state: LiveChatSessionState;
|
|
99
|
+
send(body: string): Promise<ChatMessage>;
|
|
100
|
+
/** Listener fires on every snapshot change. Returns unsubscribe. */
|
|
101
|
+
subscribe(listener: (s: LiveChatSessionSnapshot) => void): () => void;
|
|
102
|
+
getSnapshot(): LiveChatSessionSnapshot;
|
|
103
|
+
/** Stop polling, abort in-flight work, drop listeners. Idempotent. */
|
|
104
|
+
close(): void;
|
|
105
|
+
/** Drop the stored conversation token and start a fresh thread —
|
|
106
|
+
* identity switch on a shared browser. */
|
|
107
|
+
reset(): Promise<void>;
|
|
108
|
+
};
|
|
109
|
+
|
|
6
110
|
/**
|
|
7
111
|
* `createBrandfineClient` — the SDK's entry point.
|
|
8
112
|
*
|
|
@@ -164,6 +268,14 @@ type CreateAppointmentRequestInput = {
|
|
|
164
268
|
requestedAt: string;
|
|
165
269
|
/** Optional cookie-derived session id from the consumer site. */
|
|
166
270
|
visitorSessionId?: string;
|
|
271
|
+
/** Signed host-app identity — the SAME payload shape and signature
|
|
272
|
+
* Live Chat accepts, so one `liveChat.identityToken(externalId)`
|
|
273
|
+
* call signs for both plugins. When present and valid, the booking
|
|
274
|
+
* is attributed to the person (verified name/email take precedence
|
|
275
|
+
* over the free-text fields above, and the booking links to their
|
|
276
|
+
* chat threads via the shared externalId). Invalid or absent →
|
|
277
|
+
* the booking proceeds anonymously — it is never rejected. */
|
|
278
|
+
visitor?: VerifiedVisitor;
|
|
167
279
|
};
|
|
168
280
|
type CreatedAppointmentRequest = {
|
|
169
281
|
id: string;
|
|
@@ -171,11 +283,40 @@ type CreatedAppointmentRequest = {
|
|
|
171
283
|
requestedAt: string;
|
|
172
284
|
durationMinutes: number;
|
|
173
285
|
status: 'PENDING';
|
|
286
|
+
/** True iff `visitor.identityToken` HMAC-verified — your signal
|
|
287
|
+
* that the booking landed attributed rather than anonymous. */
|
|
288
|
+
identityVerified: boolean;
|
|
174
289
|
/** Visitor's self-cancel token. Embed it in confirmation
|
|
175
290
|
* emails / on-page UI so the visitor can cancel without an
|
|
176
291
|
* account. One-time use; revoked once any party acts. */
|
|
177
292
|
cancellationToken: string | null;
|
|
178
293
|
};
|
|
294
|
+
type AppointmentStatus = 'PENDING' | 'CONFIRMED' | 'REJECTED' | 'CANCELLED';
|
|
295
|
+
/** One appointment as the identity-scoped read-back returns it —
|
|
296
|
+
* the visitor-safe projection ("where does my request stand?"). */
|
|
297
|
+
type Appointment = {
|
|
298
|
+
id: string;
|
|
299
|
+
status: AppointmentStatus;
|
|
300
|
+
/** UTC ISO 8601 of the requested slot start (reflects the current
|
|
301
|
+
* slot after a reschedule). */
|
|
302
|
+
requestedAt: string;
|
|
303
|
+
/** The confirmed slot — non-null only once CONFIRMED. */
|
|
304
|
+
scheduledAt: string | null;
|
|
305
|
+
durationMinutes: number;
|
|
306
|
+
/** Workspace's IANA timezone for local rendering. */
|
|
307
|
+
timezone: string;
|
|
308
|
+
/** Customer's note — populated only on REJECTED. */
|
|
309
|
+
declineReason: string | null;
|
|
310
|
+
rescheduleCount: number;
|
|
311
|
+
respondedAt: string | null;
|
|
312
|
+
createdAt: string;
|
|
313
|
+
};
|
|
314
|
+
/** Proof of identity for read-back calls: the same externalId +
|
|
315
|
+
* `liveChat.identityToken(externalId)` pair used when booking. */
|
|
316
|
+
type AppointmentIdentity = {
|
|
317
|
+
externalId: string;
|
|
318
|
+
identityToken: string;
|
|
319
|
+
};
|
|
179
320
|
type AppointmentsApi = {
|
|
180
321
|
/**
|
|
181
322
|
* Available slots for the workspace's booking window.
|
|
@@ -191,12 +332,37 @@ type AppointmentsApi = {
|
|
|
191
332
|
* the slot is still bookable; if it isn't, throws
|
|
192
333
|
* `BrandfineApiError` with status 404 / 409.
|
|
193
334
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
335
|
+
* Pass `visitor` (signed with `liveChat.identityToken()`) to book
|
|
336
|
+
* as a known person — that unlocks `list`/`get` read-back below.
|
|
337
|
+
* Anonymous bookings remain fully supported; their status flow
|
|
338
|
+
* stays email-driven via the cancellation-token link.
|
|
198
339
|
*/
|
|
199
340
|
createRequest: (input: CreateAppointmentRequestInput) => Promise<CreatedAppointmentRequest>;
|
|
341
|
+
/**
|
|
342
|
+
* Every appointment belonging to the verified person, newest
|
|
343
|
+
* first. Requires a valid identity signature — throws
|
|
344
|
+
* `BrandfineApiError` 401 on a bad one (reads need proof; there
|
|
345
|
+
* is no anonymous downgrade for reading history). Server-side
|
|
346
|
+
* only, like `identityToken()` itself.
|
|
347
|
+
*/
|
|
348
|
+
list: (identity: AppointmentIdentity) => Promise<Appointment[]>;
|
|
349
|
+
/**
|
|
350
|
+
* One appointment by id, identity-scoped. 404s when the id does
|
|
351
|
+
* not exist OR belongs to someone else — indistinguishable by
|
|
352
|
+
* design.
|
|
353
|
+
*/
|
|
354
|
+
get: (id: string, identity: AppointmentIdentity) => Promise<Appointment>;
|
|
355
|
+
/**
|
|
356
|
+
* Spend the one-time `cancellationToken` from `createRequest` to
|
|
357
|
+
* cancel a still-PENDING request. The token IS the credential —
|
|
358
|
+
* no id or API key needed. Throws `BrandfineApiError` 400 when
|
|
359
|
+
* the request is no longer PENDING, 404 when the token is
|
|
360
|
+
* unknown/already spent.
|
|
361
|
+
*/
|
|
362
|
+
cancel: (cancellationToken: string) => Promise<{
|
|
363
|
+
id: string;
|
|
364
|
+
status: 'CANCELLED';
|
|
365
|
+
}>;
|
|
200
366
|
};
|
|
201
367
|
type AnalyticsConfig = {
|
|
202
368
|
enabled: false;
|
|
@@ -368,6 +534,12 @@ type LiveChatBootstrap = {
|
|
|
368
534
|
theme: Record<string, string> | null;
|
|
369
535
|
/** Widget bundle path relative to the API base URL. */
|
|
370
536
|
scriptPath: string;
|
|
537
|
+
/** Realtime endpoint (wss://…). OPTIONAL by design — this is
|
|
538
|
+
* what makes the upgrade non-breaking: an older SDK ignores
|
|
539
|
+
* it, a newer SDK against an older API sees it absent and
|
|
540
|
+
* polls. Consumers pass `config` through wholesale, so it
|
|
541
|
+
* reaches the browser with no change on their side. */
|
|
542
|
+
realtimeUrl?: string;
|
|
371
543
|
};
|
|
372
544
|
type LiveChatInstallResult = {
|
|
373
545
|
installed: false;
|
|
@@ -383,6 +555,13 @@ type LiveChatInstallResult = {
|
|
|
383
555
|
* the Brandfine API verifies the signature. An invalid or missing
|
|
384
556
|
* token silently downgrades the conversation to anonymous.
|
|
385
557
|
*/
|
|
558
|
+
/**
|
|
559
|
+
* A signed host-app identity payload. Named per-plugin below for
|
|
560
|
+
* discoverability, but it is ONE shape signed ONE way: a token from
|
|
561
|
+
* `liveChat.identityToken(externalId)` is accepted by Live Chat AND
|
|
562
|
+
* Appointments (`createRequest.visitor`, `list`/`get`).
|
|
563
|
+
*/
|
|
564
|
+
type VerifiedVisitor = LiveChatVisitor;
|
|
386
565
|
type LiveChatVisitor = {
|
|
387
566
|
/** Your app's stable id for this person (user id, lead reference…).
|
|
388
567
|
* Conversations sharing an externalId are the same person across
|
|
@@ -415,6 +594,14 @@ type LiveChatInstallOptions = {
|
|
|
415
594
|
* onto the widget host, it performs no crypto.
|
|
416
595
|
*/
|
|
417
596
|
visitor?: LiveChatVisitor;
|
|
597
|
+
/**
|
|
598
|
+
* Locale for the widget's visitor-facing strings (greeting, away
|
|
599
|
+
* message), e.g. your i18n router's active locale. Optional — the
|
|
600
|
+
* widget falls back to the page's `<html lang>` and then the
|
|
601
|
+
* browser language. Resolved server-side against the workspace's
|
|
602
|
+
* configured translations; unknown locales get the default text.
|
|
603
|
+
*/
|
|
604
|
+
locale?: string;
|
|
418
605
|
};
|
|
419
606
|
type LiveChatApi = {
|
|
420
607
|
/**
|
|
@@ -439,18 +626,61 @@ type LiveChatApi = {
|
|
|
439
626
|
install: (opts: LiveChatInstallOptions) => Promise<LiveChatInstallResult>;
|
|
440
627
|
/**
|
|
441
628
|
* Computes the visitor identity token:
|
|
442
|
-
* hex(HMAC_SHA256(
|
|
443
|
-
* throws in a browser context
|
|
444
|
-
*
|
|
445
|
-
* rather than ever emitting an unsigned/mis-signed payload.
|
|
629
|
+
* hex(HMAC_SHA256(signingSecret, externalId)). SERVER-ONLY — it
|
|
630
|
+
* throws in a browser context rather than ever computing next to
|
|
631
|
+
* the DOM.
|
|
446
632
|
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
633
|
+
* Signing secret, in order: `{ secret }` option →
|
|
634
|
+
* `BRANDFINE_LIVE_CHAT_IDENTITY_SECRET` env → **derived from the
|
|
635
|
+
* client's API key** (HMAC with a fixed domain-separation
|
|
636
|
+
* constant; the Brandfine API derives the same value from its
|
|
637
|
+
* stored copy). The derived path needs ZERO extra configuration —
|
|
638
|
+
* an explicit secret is only for signers that shouldn't hold the
|
|
639
|
+
* broad key, or legacy workspaces without a revealable key
|
|
640
|
+
* (generate one in the CMS: Plugins → Live Chat → Integrate).
|
|
450
641
|
*/
|
|
451
642
|
identityToken: (externalId: string, opts?: {
|
|
452
643
|
secret?: string;
|
|
453
644
|
}) => Promise<string>;
|
|
645
|
+
/**
|
|
646
|
+
* Headless session — the conversation without the widget, for
|
|
647
|
+
* building your own inline chat UI. Browser-only. Takes the same
|
|
648
|
+
* `config` and signed `visitor` as `install()` (identical identity
|
|
649
|
+
* rules and thread continuity — it IS the same conversation the
|
|
650
|
+
* widget would join, sharing its storage keys). Transport today is
|
|
651
|
+
* polling (`pollIntervalMs`, default 5000); a realtime upgrade
|
|
652
|
+
* will keep this API and the poll as fallback.
|
|
653
|
+
*/
|
|
654
|
+
createSession: (opts: LiveChatCreateSessionOptions) => Promise<LiveChatSession>;
|
|
655
|
+
/**
|
|
656
|
+
* Raw, stateless wire methods for full control. NOTE on auth: the
|
|
657
|
+
* config + start endpoints authenticate with this client's
|
|
658
|
+
* `apiKey` header — in a browser, construct the client with the
|
|
659
|
+
* PUBLISHABLE key (`createBrandfineClient({ apiKey: config.publishableKey })`),
|
|
660
|
+
* never the broad key. Message endpoints authenticate with the
|
|
661
|
+
* conversation token alone — treat it as a bearer credential:
|
|
662
|
+
* don't log it, don't put it in URLs you share.
|
|
663
|
+
*/
|
|
664
|
+
runtimeConfig: (locale?: string) => Promise<LiveChatRuntimeConfig>;
|
|
665
|
+
startConversation: (input: {
|
|
666
|
+
visitorSessionId: string;
|
|
667
|
+
conversationToken?: string;
|
|
668
|
+
pageUrl?: string;
|
|
669
|
+
referrer?: string;
|
|
670
|
+
locale?: string;
|
|
671
|
+
visitor?: Record<string, unknown>;
|
|
672
|
+
}) => Promise<StartConversationResult>;
|
|
673
|
+
sendMessage: (conversationToken: string, input: {
|
|
674
|
+
body: string;
|
|
675
|
+
clientId?: string;
|
|
676
|
+
}) => Promise<ChatMessage>;
|
|
677
|
+
history: (conversationToken: string, opts?: {
|
|
678
|
+
after?: string;
|
|
679
|
+
afterEvent?: number;
|
|
680
|
+
}) => Promise<{
|
|
681
|
+
messages: ChatMessage[];
|
|
682
|
+
status: ConversationStatus;
|
|
683
|
+
}>;
|
|
454
684
|
};
|
|
455
685
|
declare function createBrandfineClient(config: BrandfineClientConfig): BrandfineClient;
|
|
456
686
|
|
|
@@ -468,4 +698,4 @@ declare function createBrandfineClient(config: BrandfineClientConfig): Brandfine
|
|
|
468
698
|
*/
|
|
469
699
|
declare const SDK_VERSION: "0.0.0";
|
|
470
700
|
|
|
471
|
-
export { type AnalyticsConfig, type AnalyticsInstallResult, type AnalyticsOverview, type AnalyticsOverviewRange, BrandfineApiError, BrandfineCategory, type BrandfineClient, type BrandfineClientConfig, BrandfineNavigation, BrandfinePost, BrandfineWorkspace, type CreateSubmissionInput, type InstallOptions, ListCategoriesOptions, ListPostsOptions, type LiveChatBootstrap, type LiveChatInstallOptions, type LiveChatInstallResult, type LiveChatVisitor, SDK_VERSION, type Submission, createBrandfineClient };
|
|
701
|
+
export { type AnalyticsConfig, type AnalyticsInstallResult, type AnalyticsOverview, type AnalyticsOverviewRange, type Appointment, type AppointmentAvailability, type AppointmentIdentity, type AppointmentSlot, type AppointmentStatus, BrandfineApiError, BrandfineCategory, type BrandfineClient, type BrandfineClientConfig, BrandfineNavigation, BrandfinePost, BrandfineWorkspace, type ChatMessage, type ChatMessageSender, type ConversationStatus, type CreateAppointmentRequestInput, type CreateSubmissionInput, type CreatedAppointmentRequest, type InstallOptions, ListCategoriesOptions, ListPostsOptions, type LiveChatBootstrap, type LiveChatCreateSessionOptions, type LiveChatInstallOptions, type LiveChatInstallResult, type LiveChatRuntimeConfig, type LiveChatSession, type LiveChatSessionSnapshot, type LiveChatSessionState, type LiveChatVisitor, SDK_VERSION, type StartConversationResult, type Submission, type VerifiedVisitor, createBrandfineClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,110 @@ export { e as BrandfineNavItem, f as BrandfineNavItemType, g as BrandfineNavPost
|
|
|
3
3
|
export { Cache, CacheOptions, KeyedCache, KeyedCacheOptions, createCache, createKeyedCache } from './cache/index.js';
|
|
4
4
|
export { BrandfineWebhookEvent, BrandfineWebhookHandlerOptions, BrandfineWebhookPayload, createBrandfineWebhookHandler, parseWebhookPayload, verifyWebhookSecret } from './webhook/index.js';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Headless Live Chat session — the conversation WITHOUT the widget.
|
|
8
|
+
*
|
|
9
|
+
* `bf.liveChat.createSession()` gives a consumer everything needed to
|
|
10
|
+
* render chat inline in their own design system: transcript state, a
|
|
11
|
+
* send method, and a subscribe API shaped for
|
|
12
|
+
* `useSyncExternalStore` / Svelte stores / Vue refs. Brandfine keeps
|
|
13
|
+
* owning transport, identity, threading and storage; the consumer
|
|
14
|
+
* owns pixels.
|
|
15
|
+
*
|
|
16
|
+
* Storage keys are IDENTICAL to the floating widget's, so a visitor
|
|
17
|
+
* who talks through the widget on one page and an inline panel on
|
|
18
|
+
* another continues the same thread.
|
|
19
|
+
*/
|
|
20
|
+
type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM';
|
|
21
|
+
type ChatMessage = {
|
|
22
|
+
id: string;
|
|
23
|
+
body: string;
|
|
24
|
+
sender: ChatMessageSender;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
/** Monotonic per-conversation sequence — the realtime resume
|
|
27
|
+
* cursor. Optional: older APIs don't send it. */
|
|
28
|
+
eventId?: number;
|
|
29
|
+
};
|
|
30
|
+
type ConversationStatus = 'OPEN' | 'CLOSED';
|
|
31
|
+
type LiveChatSessionState = 'CONNECTING' | 'OPEN' | 'CLOSED' | 'ERROR';
|
|
32
|
+
/** The `/external/live-chat/config` shape — the RUNTIME config the
|
|
33
|
+
* widget fetches per page load. Distinct from the server-side
|
|
34
|
+
* `LiveChatBootstrap` (which additionally carries the publishable
|
|
35
|
+
* key + script path). */
|
|
36
|
+
type LiveChatRuntimeConfig = {
|
|
37
|
+
enabled: false;
|
|
38
|
+
} | {
|
|
39
|
+
enabled: true;
|
|
40
|
+
greeting: string | null;
|
|
41
|
+
offlineMessage: string | null;
|
|
42
|
+
theme: Record<string, string> | null;
|
|
43
|
+
online?: boolean;
|
|
44
|
+
/** Realtime endpoint (wss://…). Absent = poll (older API, or
|
|
45
|
+
* the transport is off). The session upgrades automatically
|
|
46
|
+
* when present; consumers never touch it. */
|
|
47
|
+
realtimeUrl?: string;
|
|
48
|
+
};
|
|
49
|
+
type LiveChatSessionSnapshot = {
|
|
50
|
+
messages: ChatMessage[];
|
|
51
|
+
status: LiveChatSessionState;
|
|
52
|
+
/** Agents available right now (business-hours based today; agent
|
|
53
|
+
* presence once the realtime transport lands). */
|
|
54
|
+
online: boolean;
|
|
55
|
+
/** Localized via the session's `locale`. */
|
|
56
|
+
greeting: string | null;
|
|
57
|
+
offlineMessage: string | null;
|
|
58
|
+
/** An outbound `send()` is in flight. */
|
|
59
|
+
sending: boolean;
|
|
60
|
+
error: Error | null;
|
|
61
|
+
};
|
|
62
|
+
type StartConversationResult = {
|
|
63
|
+
enabled: false;
|
|
64
|
+
} | {
|
|
65
|
+
enabled: true;
|
|
66
|
+
conversationId: string;
|
|
67
|
+
conversationToken: string;
|
|
68
|
+
greeting: string | null;
|
|
69
|
+
resumed: boolean;
|
|
70
|
+
online?: boolean;
|
|
71
|
+
};
|
|
72
|
+
type LiveChatCreateSessionOptions = {
|
|
73
|
+
/** The server-half bootstrap (same object `install()` takes) —
|
|
74
|
+
* supplies the publishable key (and, when the API advertises it,
|
|
75
|
+
* the realtime endpoint — passed through wholesale). */
|
|
76
|
+
config: {
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
publishableKey?: string;
|
|
79
|
+
realtimeUrl?: string;
|
|
80
|
+
};
|
|
81
|
+
/** Signed identity — SAME shape and rules as `install()`. A bad
|
|
82
|
+
* signature downgrades to anonymous server-side; it is never a
|
|
83
|
+
* second identity path. */
|
|
84
|
+
visitor?: Record<string, unknown>;
|
|
85
|
+
/** BCP-47 tag for greeting/away localization. Defaults to
|
|
86
|
+
* `<html lang>` then browser language, like the widget. */
|
|
87
|
+
locale?: string;
|
|
88
|
+
/** Defaults to `location.href`. */
|
|
89
|
+
pageUrl?: string;
|
|
90
|
+
referrer?: string;
|
|
91
|
+
/** Transcript refresh cadence. Polling is the transport today; a
|
|
92
|
+
* realtime upgrade will keep this as the fallback. */
|
|
93
|
+
pollIntervalMs?: number;
|
|
94
|
+
};
|
|
95
|
+
type LiveChatSession = {
|
|
96
|
+
/** Current transcript (same array identity as the latest snapshot). */
|
|
97
|
+
readonly messages: readonly ChatMessage[];
|
|
98
|
+
readonly state: LiveChatSessionState;
|
|
99
|
+
send(body: string): Promise<ChatMessage>;
|
|
100
|
+
/** Listener fires on every snapshot change. Returns unsubscribe. */
|
|
101
|
+
subscribe(listener: (s: LiveChatSessionSnapshot) => void): () => void;
|
|
102
|
+
getSnapshot(): LiveChatSessionSnapshot;
|
|
103
|
+
/** Stop polling, abort in-flight work, drop listeners. Idempotent. */
|
|
104
|
+
close(): void;
|
|
105
|
+
/** Drop the stored conversation token and start a fresh thread —
|
|
106
|
+
* identity switch on a shared browser. */
|
|
107
|
+
reset(): Promise<void>;
|
|
108
|
+
};
|
|
109
|
+
|
|
6
110
|
/**
|
|
7
111
|
* `createBrandfineClient` — the SDK's entry point.
|
|
8
112
|
*
|
|
@@ -164,6 +268,14 @@ type CreateAppointmentRequestInput = {
|
|
|
164
268
|
requestedAt: string;
|
|
165
269
|
/** Optional cookie-derived session id from the consumer site. */
|
|
166
270
|
visitorSessionId?: string;
|
|
271
|
+
/** Signed host-app identity — the SAME payload shape and signature
|
|
272
|
+
* Live Chat accepts, so one `liveChat.identityToken(externalId)`
|
|
273
|
+
* call signs for both plugins. When present and valid, the booking
|
|
274
|
+
* is attributed to the person (verified name/email take precedence
|
|
275
|
+
* over the free-text fields above, and the booking links to their
|
|
276
|
+
* chat threads via the shared externalId). Invalid or absent →
|
|
277
|
+
* the booking proceeds anonymously — it is never rejected. */
|
|
278
|
+
visitor?: VerifiedVisitor;
|
|
167
279
|
};
|
|
168
280
|
type CreatedAppointmentRequest = {
|
|
169
281
|
id: string;
|
|
@@ -171,11 +283,40 @@ type CreatedAppointmentRequest = {
|
|
|
171
283
|
requestedAt: string;
|
|
172
284
|
durationMinutes: number;
|
|
173
285
|
status: 'PENDING';
|
|
286
|
+
/** True iff `visitor.identityToken` HMAC-verified — your signal
|
|
287
|
+
* that the booking landed attributed rather than anonymous. */
|
|
288
|
+
identityVerified: boolean;
|
|
174
289
|
/** Visitor's self-cancel token. Embed it in confirmation
|
|
175
290
|
* emails / on-page UI so the visitor can cancel without an
|
|
176
291
|
* account. One-time use; revoked once any party acts. */
|
|
177
292
|
cancellationToken: string | null;
|
|
178
293
|
};
|
|
294
|
+
type AppointmentStatus = 'PENDING' | 'CONFIRMED' | 'REJECTED' | 'CANCELLED';
|
|
295
|
+
/** One appointment as the identity-scoped read-back returns it —
|
|
296
|
+
* the visitor-safe projection ("where does my request stand?"). */
|
|
297
|
+
type Appointment = {
|
|
298
|
+
id: string;
|
|
299
|
+
status: AppointmentStatus;
|
|
300
|
+
/** UTC ISO 8601 of the requested slot start (reflects the current
|
|
301
|
+
* slot after a reschedule). */
|
|
302
|
+
requestedAt: string;
|
|
303
|
+
/** The confirmed slot — non-null only once CONFIRMED. */
|
|
304
|
+
scheduledAt: string | null;
|
|
305
|
+
durationMinutes: number;
|
|
306
|
+
/** Workspace's IANA timezone for local rendering. */
|
|
307
|
+
timezone: string;
|
|
308
|
+
/** Customer's note — populated only on REJECTED. */
|
|
309
|
+
declineReason: string | null;
|
|
310
|
+
rescheduleCount: number;
|
|
311
|
+
respondedAt: string | null;
|
|
312
|
+
createdAt: string;
|
|
313
|
+
};
|
|
314
|
+
/** Proof of identity for read-back calls: the same externalId +
|
|
315
|
+
* `liveChat.identityToken(externalId)` pair used when booking. */
|
|
316
|
+
type AppointmentIdentity = {
|
|
317
|
+
externalId: string;
|
|
318
|
+
identityToken: string;
|
|
319
|
+
};
|
|
179
320
|
type AppointmentsApi = {
|
|
180
321
|
/**
|
|
181
322
|
* Available slots for the workspace's booking window.
|
|
@@ -191,12 +332,37 @@ type AppointmentsApi = {
|
|
|
191
332
|
* the slot is still bookable; if it isn't, throws
|
|
192
333
|
* `BrandfineApiError` with status 404 / 409.
|
|
193
334
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
335
|
+
* Pass `visitor` (signed with `liveChat.identityToken()`) to book
|
|
336
|
+
* as a known person — that unlocks `list`/`get` read-back below.
|
|
337
|
+
* Anonymous bookings remain fully supported; their status flow
|
|
338
|
+
* stays email-driven via the cancellation-token link.
|
|
198
339
|
*/
|
|
199
340
|
createRequest: (input: CreateAppointmentRequestInput) => Promise<CreatedAppointmentRequest>;
|
|
341
|
+
/**
|
|
342
|
+
* Every appointment belonging to the verified person, newest
|
|
343
|
+
* first. Requires a valid identity signature — throws
|
|
344
|
+
* `BrandfineApiError` 401 on a bad one (reads need proof; there
|
|
345
|
+
* is no anonymous downgrade for reading history). Server-side
|
|
346
|
+
* only, like `identityToken()` itself.
|
|
347
|
+
*/
|
|
348
|
+
list: (identity: AppointmentIdentity) => Promise<Appointment[]>;
|
|
349
|
+
/**
|
|
350
|
+
* One appointment by id, identity-scoped. 404s when the id does
|
|
351
|
+
* not exist OR belongs to someone else — indistinguishable by
|
|
352
|
+
* design.
|
|
353
|
+
*/
|
|
354
|
+
get: (id: string, identity: AppointmentIdentity) => Promise<Appointment>;
|
|
355
|
+
/**
|
|
356
|
+
* Spend the one-time `cancellationToken` from `createRequest` to
|
|
357
|
+
* cancel a still-PENDING request. The token IS the credential —
|
|
358
|
+
* no id or API key needed. Throws `BrandfineApiError` 400 when
|
|
359
|
+
* the request is no longer PENDING, 404 when the token is
|
|
360
|
+
* unknown/already spent.
|
|
361
|
+
*/
|
|
362
|
+
cancel: (cancellationToken: string) => Promise<{
|
|
363
|
+
id: string;
|
|
364
|
+
status: 'CANCELLED';
|
|
365
|
+
}>;
|
|
200
366
|
};
|
|
201
367
|
type AnalyticsConfig = {
|
|
202
368
|
enabled: false;
|
|
@@ -368,6 +534,12 @@ type LiveChatBootstrap = {
|
|
|
368
534
|
theme: Record<string, string> | null;
|
|
369
535
|
/** Widget bundle path relative to the API base URL. */
|
|
370
536
|
scriptPath: string;
|
|
537
|
+
/** Realtime endpoint (wss://…). OPTIONAL by design — this is
|
|
538
|
+
* what makes the upgrade non-breaking: an older SDK ignores
|
|
539
|
+
* it, a newer SDK against an older API sees it absent and
|
|
540
|
+
* polls. Consumers pass `config` through wholesale, so it
|
|
541
|
+
* reaches the browser with no change on their side. */
|
|
542
|
+
realtimeUrl?: string;
|
|
371
543
|
};
|
|
372
544
|
type LiveChatInstallResult = {
|
|
373
545
|
installed: false;
|
|
@@ -383,6 +555,13 @@ type LiveChatInstallResult = {
|
|
|
383
555
|
* the Brandfine API verifies the signature. An invalid or missing
|
|
384
556
|
* token silently downgrades the conversation to anonymous.
|
|
385
557
|
*/
|
|
558
|
+
/**
|
|
559
|
+
* A signed host-app identity payload. Named per-plugin below for
|
|
560
|
+
* discoverability, but it is ONE shape signed ONE way: a token from
|
|
561
|
+
* `liveChat.identityToken(externalId)` is accepted by Live Chat AND
|
|
562
|
+
* Appointments (`createRequest.visitor`, `list`/`get`).
|
|
563
|
+
*/
|
|
564
|
+
type VerifiedVisitor = LiveChatVisitor;
|
|
386
565
|
type LiveChatVisitor = {
|
|
387
566
|
/** Your app's stable id for this person (user id, lead reference…).
|
|
388
567
|
* Conversations sharing an externalId are the same person across
|
|
@@ -415,6 +594,14 @@ type LiveChatInstallOptions = {
|
|
|
415
594
|
* onto the widget host, it performs no crypto.
|
|
416
595
|
*/
|
|
417
596
|
visitor?: LiveChatVisitor;
|
|
597
|
+
/**
|
|
598
|
+
* Locale for the widget's visitor-facing strings (greeting, away
|
|
599
|
+
* message), e.g. your i18n router's active locale. Optional — the
|
|
600
|
+
* widget falls back to the page's `<html lang>` and then the
|
|
601
|
+
* browser language. Resolved server-side against the workspace's
|
|
602
|
+
* configured translations; unknown locales get the default text.
|
|
603
|
+
*/
|
|
604
|
+
locale?: string;
|
|
418
605
|
};
|
|
419
606
|
type LiveChatApi = {
|
|
420
607
|
/**
|
|
@@ -439,18 +626,61 @@ type LiveChatApi = {
|
|
|
439
626
|
install: (opts: LiveChatInstallOptions) => Promise<LiveChatInstallResult>;
|
|
440
627
|
/**
|
|
441
628
|
* Computes the visitor identity token:
|
|
442
|
-
* hex(HMAC_SHA256(
|
|
443
|
-
* throws in a browser context
|
|
444
|
-
*
|
|
445
|
-
* rather than ever emitting an unsigned/mis-signed payload.
|
|
629
|
+
* hex(HMAC_SHA256(signingSecret, externalId)). SERVER-ONLY — it
|
|
630
|
+
* throws in a browser context rather than ever computing next to
|
|
631
|
+
* the DOM.
|
|
446
632
|
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
633
|
+
* Signing secret, in order: `{ secret }` option →
|
|
634
|
+
* `BRANDFINE_LIVE_CHAT_IDENTITY_SECRET` env → **derived from the
|
|
635
|
+
* client's API key** (HMAC with a fixed domain-separation
|
|
636
|
+
* constant; the Brandfine API derives the same value from its
|
|
637
|
+
* stored copy). The derived path needs ZERO extra configuration —
|
|
638
|
+
* an explicit secret is only for signers that shouldn't hold the
|
|
639
|
+
* broad key, or legacy workspaces without a revealable key
|
|
640
|
+
* (generate one in the CMS: Plugins → Live Chat → Integrate).
|
|
450
641
|
*/
|
|
451
642
|
identityToken: (externalId: string, opts?: {
|
|
452
643
|
secret?: string;
|
|
453
644
|
}) => Promise<string>;
|
|
645
|
+
/**
|
|
646
|
+
* Headless session — the conversation without the widget, for
|
|
647
|
+
* building your own inline chat UI. Browser-only. Takes the same
|
|
648
|
+
* `config` and signed `visitor` as `install()` (identical identity
|
|
649
|
+
* rules and thread continuity — it IS the same conversation the
|
|
650
|
+
* widget would join, sharing its storage keys). Transport today is
|
|
651
|
+
* polling (`pollIntervalMs`, default 5000); a realtime upgrade
|
|
652
|
+
* will keep this API and the poll as fallback.
|
|
653
|
+
*/
|
|
654
|
+
createSession: (opts: LiveChatCreateSessionOptions) => Promise<LiveChatSession>;
|
|
655
|
+
/**
|
|
656
|
+
* Raw, stateless wire methods for full control. NOTE on auth: the
|
|
657
|
+
* config + start endpoints authenticate with this client's
|
|
658
|
+
* `apiKey` header — in a browser, construct the client with the
|
|
659
|
+
* PUBLISHABLE key (`createBrandfineClient({ apiKey: config.publishableKey })`),
|
|
660
|
+
* never the broad key. Message endpoints authenticate with the
|
|
661
|
+
* conversation token alone — treat it as a bearer credential:
|
|
662
|
+
* don't log it, don't put it in URLs you share.
|
|
663
|
+
*/
|
|
664
|
+
runtimeConfig: (locale?: string) => Promise<LiveChatRuntimeConfig>;
|
|
665
|
+
startConversation: (input: {
|
|
666
|
+
visitorSessionId: string;
|
|
667
|
+
conversationToken?: string;
|
|
668
|
+
pageUrl?: string;
|
|
669
|
+
referrer?: string;
|
|
670
|
+
locale?: string;
|
|
671
|
+
visitor?: Record<string, unknown>;
|
|
672
|
+
}) => Promise<StartConversationResult>;
|
|
673
|
+
sendMessage: (conversationToken: string, input: {
|
|
674
|
+
body: string;
|
|
675
|
+
clientId?: string;
|
|
676
|
+
}) => Promise<ChatMessage>;
|
|
677
|
+
history: (conversationToken: string, opts?: {
|
|
678
|
+
after?: string;
|
|
679
|
+
afterEvent?: number;
|
|
680
|
+
}) => Promise<{
|
|
681
|
+
messages: ChatMessage[];
|
|
682
|
+
status: ConversationStatus;
|
|
683
|
+
}>;
|
|
454
684
|
};
|
|
455
685
|
declare function createBrandfineClient(config: BrandfineClientConfig): BrandfineClient;
|
|
456
686
|
|
|
@@ -468,4 +698,4 @@ declare function createBrandfineClient(config: BrandfineClientConfig): Brandfine
|
|
|
468
698
|
*/
|
|
469
699
|
declare const SDK_VERSION: "0.0.0";
|
|
470
700
|
|
|
471
|
-
export { type AnalyticsConfig, type AnalyticsInstallResult, type AnalyticsOverview, type AnalyticsOverviewRange, BrandfineApiError, BrandfineCategory, type BrandfineClient, type BrandfineClientConfig, BrandfineNavigation, BrandfinePost, BrandfineWorkspace, type CreateSubmissionInput, type InstallOptions, ListCategoriesOptions, ListPostsOptions, type LiveChatBootstrap, type LiveChatInstallOptions, type LiveChatInstallResult, type LiveChatVisitor, SDK_VERSION, type Submission, createBrandfineClient };
|
|
701
|
+
export { type AnalyticsConfig, type AnalyticsInstallResult, type AnalyticsOverview, type AnalyticsOverviewRange, type Appointment, type AppointmentAvailability, type AppointmentIdentity, type AppointmentSlot, type AppointmentStatus, BrandfineApiError, BrandfineCategory, type BrandfineClient, type BrandfineClientConfig, BrandfineNavigation, BrandfinePost, BrandfineWorkspace, type ChatMessage, type ChatMessageSender, type ConversationStatus, type CreateAppointmentRequestInput, type CreateSubmissionInput, type CreatedAppointmentRequest, type InstallOptions, ListCategoriesOptions, ListPostsOptions, type LiveChatBootstrap, type LiveChatCreateSessionOptions, type LiveChatInstallOptions, type LiveChatInstallResult, type LiveChatRuntimeConfig, type LiveChatSession, type LiveChatSessionSnapshot, type LiveChatSessionState, type LiveChatVisitor, SDK_VERSION, type StartConversationResult, type Submission, type VerifiedVisitor, createBrandfineClient };
|