@getnexorai/sdk 0.1.2 → 0.1.4

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.
@@ -77,6 +77,8 @@ interface WorkflowRunStub {
77
77
  interface CreateLeadResponse {
78
78
  success: true;
79
79
  lead: Lead;
80
+ /** True when an existing lead was matched/merged (vs. a brand-new insert). */
81
+ existed?: boolean;
80
82
  workflow_run?: WorkflowRunStub;
81
83
  force_first_message?: {
82
84
  sent: boolean;
@@ -336,6 +338,20 @@ declare class NexorClient {
336
338
  createMeeting(input: CreateMeetingInput, options?: RequestOptions): Promise<unknown>;
337
339
  /** Push a note-taker transcript / summary / action items to a meeting. */
338
340
  createMeetingNotes(input: MeetingNotesInput, options?: RequestOptions): Promise<unknown>;
341
+ /**
342
+ * Fetch the dashboard-authored web-chat config for a workflow. Backed by
343
+ * GET /api/public/chat/config. The widget calls this once on init to learn
344
+ * whether web chat is enabled and to get the static opening message
345
+ * (`initial_message`, a RAW template that may contain {{variables}} — the
346
+ * widget substitutes them client-side before painting the greeting).
347
+ *
348
+ * Most users will not call this directly — `initChat()` handles it.
349
+ */
350
+ getChatConfig(workflowId: UUID, options?: RequestOptions): Promise<{
351
+ success: true;
352
+ enabled: boolean;
353
+ initial_message: string | null;
354
+ }>;
339
355
  /**
340
356
  * Send a turn in a browser-embedded chat session. Backed by
341
357
  * POST /api/public/chat on the Nexor API.
@@ -348,6 +364,16 @@ declare class NexorClient {
348
364
  message: string;
349
365
  system_prompt?: string;
350
366
  client_prompt?: string;
367
+ /** The opening message the widget actually displayed (keeps the agent's
368
+ * <webchat_opening> in sync with a per-page override). */
369
+ opening_shown?: string;
370
+ /** Start over as a brand-new lead: the API skips email/phone/session
371
+ * matching and mints a fresh lead with no prior conversation context. */
372
+ new_lead?: boolean;
373
+ /** Keep the same lead but start a fresh conversation episode: the API
374
+ * deactivates the prior run + thread and clears this workflow's collected
375
+ * variables, so the agent sees no prior context. */
376
+ reset_episode?: boolean;
351
377
  lead?: {
352
378
  first_name?: string;
353
379
  last_name?: string;
@@ -362,6 +388,53 @@ declare class NexorClient {
362
388
  session_id: string;
363
389
  lead_id?: UUID;
364
390
  workflow_run_id?: UUID;
391
+ /** True when the API recognised an existing lead (returning visitor). */
392
+ matched_existing?: boolean;
393
+ }>;
394
+ /**
395
+ * Create (or upsert) a lead from the web chat widget and force its FIRST
396
+ * contact to go out on a chosen channel — used when a visitor clicks
397
+ * "call me" / "email me" instead of opening the chat. Backed by
398
+ * POST /api/public/leads with `force_first_channel`.
399
+ *
400
+ * Unlike a literal `force_first_message`, `force_first_channel` pins the
401
+ * channel and lets the workflow's own agent compose the first outreach. The
402
+ * `metadata` you pass (page URL, requested channel, a note that the visitor
403
+ * asked to be contacted from the website) is stored on the lead so the agent
404
+ * has that context.
405
+ *
406
+ * Most users will not call this directly — `initChat()`'s contact-request
407
+ * form handles it.
408
+ */
409
+ requestContact(input: {
410
+ workflow_id: UUID;
411
+ first_name?: string;
412
+ last_name?: string;
413
+ email?: string;
414
+ phone?: string;
415
+ /** Force the first contact onto this channel (agent composes the message). */
416
+ force_first_channel: "call" | "email" | "whatsapp";
417
+ /** Context stored on the lead (page URL, requested channel, free-form note). */
418
+ metadata?: Record<string, unknown>;
419
+ /** Lead source label. Defaults to "web-chat" server-side handling. */
420
+ source?: string;
421
+ }, options?: RequestOptions): Promise<{
422
+ success: true;
423
+ lead: {
424
+ id: UUID;
425
+ first_name?: string;
426
+ email?: string;
427
+ phone?: string;
428
+ };
429
+ existed?: boolean;
430
+ workflow_run?: {
431
+ id: UUID;
432
+ };
433
+ force_first_channel?: {
434
+ channel: string;
435
+ applied: boolean;
436
+ };
437
+ warning?: string;
365
438
  }>;
366
439
  }
367
440
 
@@ -378,19 +451,111 @@ interface ChatLeadCapture {
378
451
  fields?: Array<"first_name" | "last_name" | "email" | "phone">;
379
452
  /** "before" gates chat behind the form; "skip" never asks. Default "before". */
380
453
  mode?: "before" | "skip";
381
- /** Label rendered above the form. */
454
+ /** Create the lead in Nexor the INSTANT the form is submitted (via POST
455
+ * /leads), instead of waiting for the visitor's first chat message. Captures
456
+ * visitors who fill the form but never send a message. Default false. */
457
+ createLeadOnSubmit?: boolean;
458
+ /** Heading rendered above the form (its own view). */
382
459
  label?: string;
383
460
  /** Submit button label. */
384
461
  submitLabel?: string;
462
+ /** Per-field placeholder overrides (for localization). */
463
+ placeholders?: Partial<Record<"first_name" | "last_name" | "email" | "phone", string>>;
464
+ /** Default country for the phone field — a dial code ("+56") or ISO ("CL").
465
+ * Sets the initial flag, prefix and max length. */
466
+ phoneCountryCode?: string;
467
+ /** Restrict the phone country selector to these ISO codes (e.g. ["CL","AR"]),
468
+ * in this order. Omit to offer the full built-in list. */
469
+ phoneCountries?: string[];
470
+ /** Validation error copy (for localization). */
471
+ errorMessages?: {
472
+ required?: string;
473
+ email?: string;
474
+ phone?: string;
475
+ };
476
+ }
477
+ /** The actions that can appear in the hover/speed-dial fan above the launcher. */
478
+ type ChatChannelKey = "whatsapp" | "call" | "email" | "instagram" | "chat";
479
+ /** Channels that can open the in-widget contact-request form. */
480
+ type ContactRequestChannel = "call" | "email" | "whatsapp";
481
+ /**
482
+ * Turns the `call` / `email` (optionally `whatsapp`) fan buttons into an
483
+ * in-widget contact-request FORM instead of a tel:/mailto: link. The visitor
484
+ * fills name + email + phone, and on submit the widget creates a lead in Nexor
485
+ * and forces the FIRST contact to go out on that channel (the workflow's agent
486
+ * composes the message), with context that they requested it from the website.
487
+ *
488
+ * All copy is per-channel so it can be localized; sensible English defaults
489
+ * apply when omitted.
490
+ */
491
+ interface ChatRequestContact {
492
+ /** Fan channels that open the form instead of navigating. Default ["call","email"]. */
493
+ channels?: ContactRequestChannel[];
494
+ /** Heading above the form, per channel. */
495
+ title?: Partial<Record<ContactRequestChannel, string>>;
496
+ /** Explainer line (e.g. "we need this info to contact you"), per channel. */
497
+ subtitle?: Partial<Record<ContactRequestChannel, string>>;
498
+ /** Submit button label, per channel. */
499
+ submitLabel?: Partial<Record<ContactRequestChannel, string>>;
500
+ /** Confirmation shown after a successful request, per channel. */
501
+ successText?: Partial<Record<ContactRequestChannel, string>>;
502
+ /** Label for the button that dismisses the success screen. Default "Done". */
503
+ doneLabel?: string;
504
+ /** Error shown when the request fails. */
505
+ errorText?: string;
506
+ }
507
+ /**
508
+ * Optional "speed-dial" channels shown when the visitor hovers (desktop) or taps
509
+ * (touch) the launcher bubble. Instead of opening the chat immediately, the
510
+ * launcher fans out a column of round buttons — WhatsApp, call, email, Instagram,
511
+ * and an "open chat" button — letting the visitor pick a channel.
512
+ *
513
+ * If only `chat` would be enabled (no other channel supplied), the fan is skipped
514
+ * and the launcher keeps its default behaviour (click opens the chat).
515
+ */
516
+ interface ChatChannels {
517
+ /** WhatsApp number in international format (e.g. "+56912345678"). Opens wa.me. */
518
+ whatsapp?: string;
519
+ /** Pre-filled WhatsApp message text. */
520
+ whatsappText?: string;
521
+ /** Phone number for the call button (e.g. "+56912345678"). Opens tel:. */
522
+ call?: string;
523
+ /** Email address. Opens mailto:. */
524
+ email?: string;
525
+ /** Instagram handle ("nexor.ai") or full profile URL. */
526
+ instagram?: string;
527
+ /** Include a button that opens the chat panel. Defaults to true. */
528
+ chat?: boolean;
529
+ /** Top-to-bottom order of the fan buttons. Defaults to the order below. */
530
+ order?: ChatChannelKey[];
531
+ /** Accessible labels / hover tooltips per channel. */
532
+ labels?: Partial<Record<ChatChannelKey, string>>;
533
+ /** Turn `call`/`email` (and optionally `whatsapp`) into an in-widget
534
+ * contact-request form that creates a lead and forces first contact on that
535
+ * channel, instead of opening a tel:/mailto: link. Omit to keep magic links. */
536
+ requestContact?: ChatRequestContact;
385
537
  }
386
538
  interface ChatInitOptions {
387
539
  /** Workflow that handles this chat conversation. Required. */
388
540
  workflowId: UUID;
389
541
  title?: string;
390
542
  subtitle?: string;
543
+ /**
544
+ * Force the opening message for this widget instance. Highest priority —
545
+ * WINS over the dashboard `initial_message` and over `greeting`. Use it for
546
+ * per-page openings (e.g. an ecommerce-specific greeting on /ecommerce).
547
+ * Supports the same {{first_name}} / {{last_name}} / {{full_name}} / {{email}}
548
+ * / {{phone}} tokens, substituted from the captured/known lead.
549
+ */
550
+ openingMessage?: string;
551
+ /** Fallback opening, used only when neither `openingMessage` nor the
552
+ * dashboard `initial_message` is set. */
391
553
  greeting?: string;
392
554
  /** Shown when a turn fails or the server returns no reply. */
393
555
  errorText?: string;
556
+ /** Shown when web chat is disabled for the workflow (channel turned off in
557
+ * the dashboard, or the API reports it unavailable). */
558
+ disabledText?: string;
394
559
  /** BCP-47 locale for timestamps (e.g. "es", "en-US"). Defaults to the browser. */
395
560
  locale?: string;
396
561
  /** Hex colour for bubble + accents. Defaults to #111827. */
@@ -399,12 +564,32 @@ interface ChatInitOptions {
399
564
  accentTextColor?: string;
400
565
  /** Show the "Powered by Nexor" footer. Defaults to true. */
401
566
  showBranding?: boolean;
567
+ /** Composer placeholder once chat is active. Default "Type a message…". */
568
+ inputPlaceholder?: string;
569
+ /** Show a header "start over" control that wipes the conversation and starts a
570
+ * BRAND-NEW lead (the backend won't re-match the old one). Default true. */
571
+ showRestart?: boolean;
572
+ /** aria-label / tooltip for the header restart control. Default "Start over". */
573
+ restartLabel?: string;
402
574
  position?: "bottom-right" | "bottom-left";
403
575
  /** Auto-open the panel on first load. Defaults to false. */
404
576
  openOnLoad?: boolean;
577
+ /**
578
+ * Turn the launcher into a hover/tap "speed-dial" that fans out channel
579
+ * buttons (WhatsApp, call, email, Instagram, chat) instead of opening the
580
+ * chat right away. Omit for the classic click-to-open launcher.
581
+ */
582
+ channels?: ChatChannels;
405
583
  /** Coalesce rapid-fire messages: wait this many ms of quiet, then send the
406
584
  * buffered messages as one combined turn. Defaults to 700. 0 = per message. */
407
585
  debounceMs?: number;
586
+ /** Split a bot reply into separate bubbles on blank lines (paragraph breaks),
587
+ * WhatsApp-style. Defaults to true. Set false to keep one bubble per reply. */
588
+ splitReplies?: boolean;
589
+ /** Fixed typing pause (ms) shown between split bubbles. Omit for a natural
590
+ * 3000–5000ms pause scaled by the length of the next message (longer text
591
+ * reads as more time spent typing). */
592
+ splitDelayMs?: number;
408
593
  /** Per-turn request timeout (ms). Agent turns can legitimately take 10-20s,
409
594
  * so this is generous (default 60000). Chat turns never retry — a timeout
410
595
  * surfaces the error once rather than re-sending the message. */
@@ -422,8 +607,18 @@ interface ChatInitOptions {
422
607
  metadata?: Record<string, unknown>;
423
608
  };
424
609
  capture?: ChatLeadCapture;
425
- /** Arbitrary metadata passed with each turn (page URL, UTM, …). */
426
- metadata?: Record<string, unknown>;
610
+ /** Arbitrary metadata passed with each turn (page URL, UTM, …). Pass a
611
+ * FUNCTION to have it resolved fresh on every turn — useful for SPA values
612
+ * that change without re-mounting the widget (e.g. the current route). */
613
+ metadata?: Record<string, unknown> | (() => Record<string, unknown> | undefined);
614
+ /** Banner text when a prior conversation is restored but we have no name.
615
+ * Default "Continuing your conversation". */
616
+ resumeText?: string;
617
+ /** Banner text when we know the visitor. `{name}` is replaced with their
618
+ * first name (or email / phone as a fallback). Default "Continuing as {name}". */
619
+ resumeWithNameText?: string;
620
+ /** Label for the reset action in the resume banner. Default "Start over". */
621
+ startOverText?: string;
427
622
  onOpen?: () => void;
428
623
  onClose?: () => void;
429
624
  onMessage?: (msg: {
@@ -445,4 +640,4 @@ interface ChatHandle {
445
640
  getSessionId(): string;
446
641
  }
447
642
 
448
- export { type AutomationInput as A, type BulkResultEntry as B, type Campaign as C, type ForceFirstMessage as F, type GetLeadHistoryParams as G, type ISODateString as I, type Lead as L, type MeetingNotesInput as M, NexorClient as N, type RequestOptions as R, type SendMessageInput as S, type UUID as U, type WhatsAppTemplate as W, type ChatHandle as a, type ChatInitOptions as b, type ChatLeadCapture as c, type ClientOptions as d, type CreateLeadBulkResponse as e, type CreateLeadInput as f, type CreateLeadResponse as g, type CreateMeetingInput as h, type GetLeadHistoryResponse as i, type GetLeadResponse as j, type IsPausedResponse as k, type LeadHistoryItem as l, type ListTemplatesParams as m, type ResumeAutomationInput as n, type SyncTagsInput as o, type SyncTagsResponse as p, type Workflow as q, type WorkflowRunStub as r };
643
+ export { type AutomationInput as A, type BulkResultEntry as B, type Campaign as C, type ForceFirstMessage as F, type GetLeadHistoryParams as G, type ISODateString as I, type Lead as L, type MeetingNotesInput as M, NexorClient as N, type RequestOptions as R, type SendMessageInput as S, type UUID as U, type WhatsAppTemplate as W, type ChatChannelKey as a, type ChatChannels as b, type ChatHandle as c, type ChatInitOptions as d, type ChatLeadCapture as e, type ClientOptions as f, type CreateLeadBulkResponse as g, type CreateLeadInput as h, type CreateLeadResponse as i, type CreateMeetingInput as j, type GetLeadHistoryResponse as k, type GetLeadResponse as l, type IsPausedResponse as m, type LeadHistoryItem as n, type ListTemplatesParams as o, type ResumeAutomationInput as p, type SyncTagsInput as q, type SyncTagsResponse as r, type Workflow as s, type WorkflowRunStub as t };
@@ -77,6 +77,8 @@ interface WorkflowRunStub {
77
77
  interface CreateLeadResponse {
78
78
  success: true;
79
79
  lead: Lead;
80
+ /** True when an existing lead was matched/merged (vs. a brand-new insert). */
81
+ existed?: boolean;
80
82
  workflow_run?: WorkflowRunStub;
81
83
  force_first_message?: {
82
84
  sent: boolean;
@@ -336,6 +338,20 @@ declare class NexorClient {
336
338
  createMeeting(input: CreateMeetingInput, options?: RequestOptions): Promise<unknown>;
337
339
  /** Push a note-taker transcript / summary / action items to a meeting. */
338
340
  createMeetingNotes(input: MeetingNotesInput, options?: RequestOptions): Promise<unknown>;
341
+ /**
342
+ * Fetch the dashboard-authored web-chat config for a workflow. Backed by
343
+ * GET /api/public/chat/config. The widget calls this once on init to learn
344
+ * whether web chat is enabled and to get the static opening message
345
+ * (`initial_message`, a RAW template that may contain {{variables}} — the
346
+ * widget substitutes them client-side before painting the greeting).
347
+ *
348
+ * Most users will not call this directly — `initChat()` handles it.
349
+ */
350
+ getChatConfig(workflowId: UUID, options?: RequestOptions): Promise<{
351
+ success: true;
352
+ enabled: boolean;
353
+ initial_message: string | null;
354
+ }>;
339
355
  /**
340
356
  * Send a turn in a browser-embedded chat session. Backed by
341
357
  * POST /api/public/chat on the Nexor API.
@@ -348,6 +364,16 @@ declare class NexorClient {
348
364
  message: string;
349
365
  system_prompt?: string;
350
366
  client_prompt?: string;
367
+ /** The opening message the widget actually displayed (keeps the agent's
368
+ * <webchat_opening> in sync with a per-page override). */
369
+ opening_shown?: string;
370
+ /** Start over as a brand-new lead: the API skips email/phone/session
371
+ * matching and mints a fresh lead with no prior conversation context. */
372
+ new_lead?: boolean;
373
+ /** Keep the same lead but start a fresh conversation episode: the API
374
+ * deactivates the prior run + thread and clears this workflow's collected
375
+ * variables, so the agent sees no prior context. */
376
+ reset_episode?: boolean;
351
377
  lead?: {
352
378
  first_name?: string;
353
379
  last_name?: string;
@@ -362,6 +388,53 @@ declare class NexorClient {
362
388
  session_id: string;
363
389
  lead_id?: UUID;
364
390
  workflow_run_id?: UUID;
391
+ /** True when the API recognised an existing lead (returning visitor). */
392
+ matched_existing?: boolean;
393
+ }>;
394
+ /**
395
+ * Create (or upsert) a lead from the web chat widget and force its FIRST
396
+ * contact to go out on a chosen channel — used when a visitor clicks
397
+ * "call me" / "email me" instead of opening the chat. Backed by
398
+ * POST /api/public/leads with `force_first_channel`.
399
+ *
400
+ * Unlike a literal `force_first_message`, `force_first_channel` pins the
401
+ * channel and lets the workflow's own agent compose the first outreach. The
402
+ * `metadata` you pass (page URL, requested channel, a note that the visitor
403
+ * asked to be contacted from the website) is stored on the lead so the agent
404
+ * has that context.
405
+ *
406
+ * Most users will not call this directly — `initChat()`'s contact-request
407
+ * form handles it.
408
+ */
409
+ requestContact(input: {
410
+ workflow_id: UUID;
411
+ first_name?: string;
412
+ last_name?: string;
413
+ email?: string;
414
+ phone?: string;
415
+ /** Force the first contact onto this channel (agent composes the message). */
416
+ force_first_channel: "call" | "email" | "whatsapp";
417
+ /** Context stored on the lead (page URL, requested channel, free-form note). */
418
+ metadata?: Record<string, unknown>;
419
+ /** Lead source label. Defaults to "web-chat" server-side handling. */
420
+ source?: string;
421
+ }, options?: RequestOptions): Promise<{
422
+ success: true;
423
+ lead: {
424
+ id: UUID;
425
+ first_name?: string;
426
+ email?: string;
427
+ phone?: string;
428
+ };
429
+ existed?: boolean;
430
+ workflow_run?: {
431
+ id: UUID;
432
+ };
433
+ force_first_channel?: {
434
+ channel: string;
435
+ applied: boolean;
436
+ };
437
+ warning?: string;
365
438
  }>;
366
439
  }
367
440
 
@@ -378,19 +451,111 @@ interface ChatLeadCapture {
378
451
  fields?: Array<"first_name" | "last_name" | "email" | "phone">;
379
452
  /** "before" gates chat behind the form; "skip" never asks. Default "before". */
380
453
  mode?: "before" | "skip";
381
- /** Label rendered above the form. */
454
+ /** Create the lead in Nexor the INSTANT the form is submitted (via POST
455
+ * /leads), instead of waiting for the visitor's first chat message. Captures
456
+ * visitors who fill the form but never send a message. Default false. */
457
+ createLeadOnSubmit?: boolean;
458
+ /** Heading rendered above the form (its own view). */
382
459
  label?: string;
383
460
  /** Submit button label. */
384
461
  submitLabel?: string;
462
+ /** Per-field placeholder overrides (for localization). */
463
+ placeholders?: Partial<Record<"first_name" | "last_name" | "email" | "phone", string>>;
464
+ /** Default country for the phone field — a dial code ("+56") or ISO ("CL").
465
+ * Sets the initial flag, prefix and max length. */
466
+ phoneCountryCode?: string;
467
+ /** Restrict the phone country selector to these ISO codes (e.g. ["CL","AR"]),
468
+ * in this order. Omit to offer the full built-in list. */
469
+ phoneCountries?: string[];
470
+ /** Validation error copy (for localization). */
471
+ errorMessages?: {
472
+ required?: string;
473
+ email?: string;
474
+ phone?: string;
475
+ };
476
+ }
477
+ /** The actions that can appear in the hover/speed-dial fan above the launcher. */
478
+ type ChatChannelKey = "whatsapp" | "call" | "email" | "instagram" | "chat";
479
+ /** Channels that can open the in-widget contact-request form. */
480
+ type ContactRequestChannel = "call" | "email" | "whatsapp";
481
+ /**
482
+ * Turns the `call` / `email` (optionally `whatsapp`) fan buttons into an
483
+ * in-widget contact-request FORM instead of a tel:/mailto: link. The visitor
484
+ * fills name + email + phone, and on submit the widget creates a lead in Nexor
485
+ * and forces the FIRST contact to go out on that channel (the workflow's agent
486
+ * composes the message), with context that they requested it from the website.
487
+ *
488
+ * All copy is per-channel so it can be localized; sensible English defaults
489
+ * apply when omitted.
490
+ */
491
+ interface ChatRequestContact {
492
+ /** Fan channels that open the form instead of navigating. Default ["call","email"]. */
493
+ channels?: ContactRequestChannel[];
494
+ /** Heading above the form, per channel. */
495
+ title?: Partial<Record<ContactRequestChannel, string>>;
496
+ /** Explainer line (e.g. "we need this info to contact you"), per channel. */
497
+ subtitle?: Partial<Record<ContactRequestChannel, string>>;
498
+ /** Submit button label, per channel. */
499
+ submitLabel?: Partial<Record<ContactRequestChannel, string>>;
500
+ /** Confirmation shown after a successful request, per channel. */
501
+ successText?: Partial<Record<ContactRequestChannel, string>>;
502
+ /** Label for the button that dismisses the success screen. Default "Done". */
503
+ doneLabel?: string;
504
+ /** Error shown when the request fails. */
505
+ errorText?: string;
506
+ }
507
+ /**
508
+ * Optional "speed-dial" channels shown when the visitor hovers (desktop) or taps
509
+ * (touch) the launcher bubble. Instead of opening the chat immediately, the
510
+ * launcher fans out a column of round buttons — WhatsApp, call, email, Instagram,
511
+ * and an "open chat" button — letting the visitor pick a channel.
512
+ *
513
+ * If only `chat` would be enabled (no other channel supplied), the fan is skipped
514
+ * and the launcher keeps its default behaviour (click opens the chat).
515
+ */
516
+ interface ChatChannels {
517
+ /** WhatsApp number in international format (e.g. "+56912345678"). Opens wa.me. */
518
+ whatsapp?: string;
519
+ /** Pre-filled WhatsApp message text. */
520
+ whatsappText?: string;
521
+ /** Phone number for the call button (e.g. "+56912345678"). Opens tel:. */
522
+ call?: string;
523
+ /** Email address. Opens mailto:. */
524
+ email?: string;
525
+ /** Instagram handle ("nexor.ai") or full profile URL. */
526
+ instagram?: string;
527
+ /** Include a button that opens the chat panel. Defaults to true. */
528
+ chat?: boolean;
529
+ /** Top-to-bottom order of the fan buttons. Defaults to the order below. */
530
+ order?: ChatChannelKey[];
531
+ /** Accessible labels / hover tooltips per channel. */
532
+ labels?: Partial<Record<ChatChannelKey, string>>;
533
+ /** Turn `call`/`email` (and optionally `whatsapp`) into an in-widget
534
+ * contact-request form that creates a lead and forces first contact on that
535
+ * channel, instead of opening a tel:/mailto: link. Omit to keep magic links. */
536
+ requestContact?: ChatRequestContact;
385
537
  }
386
538
  interface ChatInitOptions {
387
539
  /** Workflow that handles this chat conversation. Required. */
388
540
  workflowId: UUID;
389
541
  title?: string;
390
542
  subtitle?: string;
543
+ /**
544
+ * Force the opening message for this widget instance. Highest priority —
545
+ * WINS over the dashboard `initial_message` and over `greeting`. Use it for
546
+ * per-page openings (e.g. an ecommerce-specific greeting on /ecommerce).
547
+ * Supports the same {{first_name}} / {{last_name}} / {{full_name}} / {{email}}
548
+ * / {{phone}} tokens, substituted from the captured/known lead.
549
+ */
550
+ openingMessage?: string;
551
+ /** Fallback opening, used only when neither `openingMessage` nor the
552
+ * dashboard `initial_message` is set. */
391
553
  greeting?: string;
392
554
  /** Shown when a turn fails or the server returns no reply. */
393
555
  errorText?: string;
556
+ /** Shown when web chat is disabled for the workflow (channel turned off in
557
+ * the dashboard, or the API reports it unavailable). */
558
+ disabledText?: string;
394
559
  /** BCP-47 locale for timestamps (e.g. "es", "en-US"). Defaults to the browser. */
395
560
  locale?: string;
396
561
  /** Hex colour for bubble + accents. Defaults to #111827. */
@@ -399,12 +564,32 @@ interface ChatInitOptions {
399
564
  accentTextColor?: string;
400
565
  /** Show the "Powered by Nexor" footer. Defaults to true. */
401
566
  showBranding?: boolean;
567
+ /** Composer placeholder once chat is active. Default "Type a message…". */
568
+ inputPlaceholder?: string;
569
+ /** Show a header "start over" control that wipes the conversation and starts a
570
+ * BRAND-NEW lead (the backend won't re-match the old one). Default true. */
571
+ showRestart?: boolean;
572
+ /** aria-label / tooltip for the header restart control. Default "Start over". */
573
+ restartLabel?: string;
402
574
  position?: "bottom-right" | "bottom-left";
403
575
  /** Auto-open the panel on first load. Defaults to false. */
404
576
  openOnLoad?: boolean;
577
+ /**
578
+ * Turn the launcher into a hover/tap "speed-dial" that fans out channel
579
+ * buttons (WhatsApp, call, email, Instagram, chat) instead of opening the
580
+ * chat right away. Omit for the classic click-to-open launcher.
581
+ */
582
+ channels?: ChatChannels;
405
583
  /** Coalesce rapid-fire messages: wait this many ms of quiet, then send the
406
584
  * buffered messages as one combined turn. Defaults to 700. 0 = per message. */
407
585
  debounceMs?: number;
586
+ /** Split a bot reply into separate bubbles on blank lines (paragraph breaks),
587
+ * WhatsApp-style. Defaults to true. Set false to keep one bubble per reply. */
588
+ splitReplies?: boolean;
589
+ /** Fixed typing pause (ms) shown between split bubbles. Omit for a natural
590
+ * 3000–5000ms pause scaled by the length of the next message (longer text
591
+ * reads as more time spent typing). */
592
+ splitDelayMs?: number;
408
593
  /** Per-turn request timeout (ms). Agent turns can legitimately take 10-20s,
409
594
  * so this is generous (default 60000). Chat turns never retry — a timeout
410
595
  * surfaces the error once rather than re-sending the message. */
@@ -422,8 +607,18 @@ interface ChatInitOptions {
422
607
  metadata?: Record<string, unknown>;
423
608
  };
424
609
  capture?: ChatLeadCapture;
425
- /** Arbitrary metadata passed with each turn (page URL, UTM, …). */
426
- metadata?: Record<string, unknown>;
610
+ /** Arbitrary metadata passed with each turn (page URL, UTM, …). Pass a
611
+ * FUNCTION to have it resolved fresh on every turn — useful for SPA values
612
+ * that change without re-mounting the widget (e.g. the current route). */
613
+ metadata?: Record<string, unknown> | (() => Record<string, unknown> | undefined);
614
+ /** Banner text when a prior conversation is restored but we have no name.
615
+ * Default "Continuing your conversation". */
616
+ resumeText?: string;
617
+ /** Banner text when we know the visitor. `{name}` is replaced with their
618
+ * first name (or email / phone as a fallback). Default "Continuing as {name}". */
619
+ resumeWithNameText?: string;
620
+ /** Label for the reset action in the resume banner. Default "Start over". */
621
+ startOverText?: string;
427
622
  onOpen?: () => void;
428
623
  onClose?: () => void;
429
624
  onMessage?: (msg: {
@@ -445,4 +640,4 @@ interface ChatHandle {
445
640
  getSessionId(): string;
446
641
  }
447
642
 
448
- export { type AutomationInput as A, type BulkResultEntry as B, type Campaign as C, type ForceFirstMessage as F, type GetLeadHistoryParams as G, type ISODateString as I, type Lead as L, type MeetingNotesInput as M, NexorClient as N, type RequestOptions as R, type SendMessageInput as S, type UUID as U, type WhatsAppTemplate as W, type ChatHandle as a, type ChatInitOptions as b, type ChatLeadCapture as c, type ClientOptions as d, type CreateLeadBulkResponse as e, type CreateLeadInput as f, type CreateLeadResponse as g, type CreateMeetingInput as h, type GetLeadHistoryResponse as i, type GetLeadResponse as j, type IsPausedResponse as k, type LeadHistoryItem as l, type ListTemplatesParams as m, type ResumeAutomationInput as n, type SyncTagsInput as o, type SyncTagsResponse as p, type Workflow as q, type WorkflowRunStub as r };
643
+ export { type AutomationInput as A, type BulkResultEntry as B, type Campaign as C, type ForceFirstMessage as F, type GetLeadHistoryParams as G, type ISODateString as I, type Lead as L, type MeetingNotesInput as M, NexorClient as N, type RequestOptions as R, type SendMessageInput as S, type UUID as U, type WhatsAppTemplate as W, type ChatChannelKey as a, type ChatChannels as b, type ChatHandle as c, type ChatInitOptions as d, type ChatLeadCapture as e, type ClientOptions as f, type CreateLeadBulkResponse as g, type CreateLeadInput as h, type CreateLeadResponse as i, type CreateMeetingInput as j, type GetLeadHistoryResponse as k, type GetLeadResponse as l, type IsPausedResponse as m, type LeadHistoryItem as n, type ListTemplatesParams as o, type ResumeAutomationInput as p, type SyncTagsInput as q, type SyncTagsResponse as r, type Workflow as s, type WorkflowRunStub as t };