On this page
Where options come from#
The widget's look and wording come from three places, strongest first:
- Options in your page:
data-*attributes,data-config, or the object passed toinit()orwidget.update(). - Settings saved in the Widget Studio (your portal). The widget fetches them from Recent Visits when the page loads. Production shows the published settings; Test shows your draft.
- Built-in defaults.
Options are merged field by field: a value in your page replaces only that field, and anything you leave out falls back to the next layer. Everything is validated by the widget: an invalid value is replaced by the safe default and a warning is written to the browser console (Recent Visits widget: …). None of these options grants access to anything; the agency, its tours and what the assistant may say come from the server for your key.
Core options#
These control how the widget starts. They are available to init() (and, for the script tag, as the attributes shown).
| Option | Type | Default | Required | Description |
|---|---|---|---|---|
key (data-key) | string, rvk_… | none | Yes | Your integration's public key. Must match rvk_ plus 22 to 64 letters, digits, _ or -; otherwise init throws Recent Visits: invalid key. It identifies your integration and is not a secret. |
apiBase (data-api-base) | URL string | https://recent-visit.com | No | The Recent Visits address. Only for staging or local development. Must be https, or http on localhost/127.0.0.1; no credentials. |
storage | 'session' | 'memory' | 'session' | No | Where the session and the visible chat are kept. session uses the tab's sessionStorage, so a reload or page change keeps the same conversation. memory keeps nothing across page loads. Never localStorage or cookies. |
persist | boolean | true when storage is session | No | Set false to stop saving the visible chat in the tab. |
getChallengeToken | () => Promise<string> | none | No | Rarely needed. The widget runs Recent Visits' human check itself when the server asks for it (see Security); only set this to supply the token yourself. Called after the server asks for a check, and once more with a fresh token if the first is refused. |
Appearance and wording#
| Option | Type | Default | Limits and notes |
|---|---|---|---|
appearance | 'light' | 'dark' | 'auto' | 'auto' | auto follows the visitor's system and changes live. |
theme | object | see Themes | Colours, corner radius and font. |
themeDark | object | {} | Colour overrides for dark mode. |
accent | colour | none | Legacy shortcut for theme.primary. |
position | 'right' | 'left' | 'right' | Which side of the screen. |
offset | { x?: number, y?: number } | { x: 16, y: 16 } | Distance from the screen edge in px, 0 to 200. |
launcher.label | string | none (icon only) | Plain text, up to 30 characters. |
launcher.size | number | 56 | 44 to 72 px. |
launcher.icon | 'compass' | 'chat' | 'sparkle' | 'compass' | Built-in icons. |
launcher.avatarUrl | URL | none | An https image, or a small base64 PNG, JPEG or WebP. |
agency.name | string | your agency name from the server | Up to 80 characters. |
agency.logoUrl | URL | your profile logo, if the Studio's logo switch is on | https image or small base64 PNG, JPEG or WebP. |
assistant.name | string | AI Travel Assistant | Up to 40 characters. |
assistant.avatarUrl | URL | none | As launcher.avatarUrl. Shown in the chat header when there is no agency.logoUrl. |
header.title | string | the agency name | Up to 60 characters. |
header.subtitle | string | AI travel assistant | Up to 80 characters. |
welcomeMessage | string | the greeting set in the Studio, or a default | Up to 400 characters. |
suggestedQuestions | string[] | none | At most 4, each up to 70 characters. |
locale | 'en' | 'en' | English is the only supported language today. |
Text options are plain text: control characters and angle brackets are removed and the text is clipped to the limit. Images must be https (or a small base64 raster image) and must be allowed by your site's Content-Security-Policy.
Features and page context#
| Option | Type | Default | Description |
|---|---|---|---|
features | { chat?, tours?, quote?, talkToAgency? } (booleans) | all true | Set one to false to hide it. It cannot turn on a feature your integration does not offer (the Studio's Features tab decides that for chat, tours and quote; Behavior → Talk to a person decides it for talkToAgency). |
pageContext.tourSlug | string (a slug) | none | On a page about one of your tours, pass its slug to get tailored suggestions. The server accepts it only if it is your published tour. A malformed slug is ignored. |
pageContext.title | string | none | Up to 100 characters. Context you choose to share; it is never scraped from the page. |
Notifications and invitations#
The widget never uses browser push notifications and never asks for permission. A small invitation next to the launcher is off unless you turn it on.
| Option | Type | Default | Limits |
|---|---|---|---|
notifications.enabled | boolean | false | Must be true to show any invitation. |
notifications.welcome | string | false | Planning a trip? Ask me anything. | Up to 120 characters; false turns the welcome text off. |
notifications.delayMs | number | 10000 | 1,000 to 120,000. |
notifications.invitations | array | none | Up to 5 contextual invitations; first match wins. Each has text (up to 120 characters) and one of pathStartsWith, pathEquals (needs useLocation) or tourPage: true; optional delayMs. |
notifications.useLocation | boolean | false | Allows path rules to read location.pathname (nothing else is read from your page). |
notifications.maxPerSession | number | 1 | 0 to 3. |
notifications.cooldownHours | number | 24 | 0 to 720. |
notifications.dismissCooldownHours | number | 168 | 0 to 2160; after the visitor closes it. |
notifications.autoHideMs | number | 15000 | 3,000 to 60,000. |
notifications.showOnMobile | boolean | false | Small screens are skipped unless true. |
notifications.persist | 'session' | 'local' | 'none' | 'session' | Where frequency and mute state are kept (only flags and timestamps, no text). |
notifications.unreadIndicator | boolean | true | A dot on the launcher for a real unread reply, never a count. |
An invitation is never shown once the visitor has opened the chat, never in a background tab, and hides itself after autoHideMs.
Example#
import type { InitOptions } from '@recent-visits/widget'
export const options: InitOptions = {
key: 'rvk_AAAAAAAAAAAAAAAAAAAAAAAA',
appearance: 'auto',
theme: { primary: '#0b5c4a', accent: '#b45309', radius: 14 },
position: 'right',
launcher: { label: 'Ask our travel guide', icon: 'compass' },
assistant: { name: 'Trek Buddy' },
header: { title: 'Ask Alpha Treks' },
welcomeMessage: 'Namaste! Ask me about treks and tours.',
suggestedQuestions: ['Explore tours', 'Create my trip plan'],
features: { talkToAgency: false },
}As a script tag the same values go in data-config as JSON:
<script
src="https://recent-visit.com/widget/1/widget.js"
data-key="rvk_AAAAAAAAAAAAAAAAAAAAAAAA"
data-config='{"theme":{"primary":"#0b5c4a","radius":14},"launcher":{"label":"Ask our travel guide"},"assistant":{"name":"Trek Buddy"}}'
async
></script>If data-config is not valid JSON the widget logs data-config is not valid JSON and was ignored and carries on.
Limits inside the chat and the quote form#
These are fixed in the built-in interface and are not options.
| Item | Limit |
|---|---|
| Chat message | Up to 2,000 characters; Enter sends, Shift+Enter adds a line. |
| Visitor first name | Optional, up to 60 characters; anything that looks like an email, phone number or link is refused. |
| Quote form message | At least 10 and at most 3,000 characters. |
| Quote form phone, travellers | Phone up to 40 characters; travellers 1 to 50. |
| Tours tab | Shows up to 12 of your published tours. |
Script tag notes#
apiBasemust be given asdata-api-base; a value insidedata-configis overridden by the attribute (which is empty when absent).getChallengeTokenis a function and cannot be passed as JSON; useinit()if you need it.data-configmust be a JSON object; an array or a non-object is ignored.
Last checked against the product:
Related articles
- Themes and agency branding · Colours, dark mode, corner radius, fonts, launcher and logo for the Recent Visits widget, how contrast is enforced, and what cannot be styled.
- Widget Studio field reference · Every screen, tab, field and button of the Website widget page in the agency portal: websites and keys, settings, draft and publish, live preview, and what owners and managers can see.
- SDK API reference · The public surface of the Recent Visits widget package: init, the widget handle, the headless client, errors, types and the HTTP endpoints they use.
- SDK examples · Copy-and-paste integration examples for the Recent Visits widget: minimal, agency configuration, themed, Next.js, React with the package, and a headless quote request.
Still stuck? Contact us and tell us what you tried.