# Bootstrap visitor access
Source: https://docs.webless.ai/api-reference/latest/agent/bootstrap
api-reference/openapi-agent-runtime.json POST /webless/v1/bootstrap
Exchange your index ID and a browser session ID for a Bearer access token. The token is bound to the Origin of the page that calls this route.
## Try it in this order
Open [Bootstrap](/api-reference/latest/agent/bootstrap). Enter your index
ID, click **Try it**, then **Send**. The playground saves your
`accessToken` for the next two pages. You can only use it on this site, and
it expires in about 15 minutes.
Open [Send a message](/api-reference/latest/agent/create-session). This page
fills in your token and index ID. Send `Hello`. The playground saves your
`sessionId` (`wrun_…`).
Open [Stream events](/api-reference/latest/agent/session-stream). This page
fills in your token and that `sessionId`. Click **Send** to read the reply.
To send another message, go back to **Send a message** and click **Send**
again. Then open **Stream events** once more. The playground updates the
session id to that new turn.
Start here. This preview calls **`https://runtime.webless.ai`**. Use an index
that is published in Agentic mode on production.
# Send a message
Source: https://docs.webless.ai/api-reference/latest/agent/create-session
api-reference/openapi-agent-runtime.json POST /eve/v1/session
Start or continue a session with a visitor message. Use the access token from bootstrap in the Authorization header. Include indexId on the query string.
## Try it in this order
Open [Bootstrap](/api-reference/latest/agent/bootstrap). Enter your index
ID, click **Try it**, then **Send**. The playground saves your
`accessToken` for the next two pages. You can only use it on this site, and
it expires in about 15 minutes.
Open [Send a message](/api-reference/latest/agent/create-session). This page
fills in your token and index ID. Send `Hello`. The playground saves your
`sessionId` (`wrun_…`).
Open [Stream events](/api-reference/latest/agent/session-stream). This page
fills in your token and that `sessionId`. Click **Send** to read the reply.
To send another message, go back to **Send a message** and click **Send**
again. Then open **Stream events** once more. The playground updates the
session id to that new turn.
This is step 2. After you bootstrap, this page fills in your token. After
**Send**, open Stream events to read the reply.
# Stream session events
Source: https://docs.webless.ai/api-reference/latest/agent/session-stream
api-reference/openapi-agent-runtime.json GET /eve/v1/session/{sessionId}/stream
NDJSON event stream for the active turn. Call after POST /eve/v1/session with the same Authorization header.
## Try it in this order
Open [Bootstrap](/api-reference/latest/agent/bootstrap). Enter your index
ID, click **Try it**, then **Send**. The playground saves your
`accessToken` for the next two pages. You can only use it on this site, and
it expires in about 15 minutes.
Open [Send a message](/api-reference/latest/agent/create-session). This page
fills in your token and index ID. Send `Hello`. The playground saves your
`sessionId` (`wrun_…`).
Open [Stream events](/api-reference/latest/agent/session-stream). This page
fills in your token and that `sessionId`. Click **Send** to read the reply.
To send another message, go back to **Send a message** and click **Send**
again. Then open **Stream events** once more. The playground updates the
session id to that new turn.
This is step 3. This page fills in your token and the latest `wrun_…`
session id. Click **Send** to read the reply from the message you just sent.
# Query data
Source: https://docs.webless.ai/api-reference/latest/data/query-data
api-reference/openapi-latest.json GET /query-data
Triggers a data export for the given company and date range.
The `presigned_url` returned by this endpoint is not always ready immediately.
Poll it with `Range: bytes=0-0` every 10 seconds until it returns `200` or
`206`, then download the full file.
# Introduction
Source: https://docs.webless.ai/api-reference/latest/introduction
Reference for the latest API
New integrations should use the `/api/v1` routes documented here.
## API surface
Use `queries`, `completions`, `cta-selection`, `suggestions`, and `health` to
power search experiences.
Generate Snackable Reads and FAQs from content already indexed for a page.
Use `shares` and `session-summaries` to create shareable history snapshots
and structured session recaps.
Bootstrap from the browser, send visitor messages, and stream agent replies
when Agentic mode is published.
## Version pointer
Latest endpoints accept a `version` field where runtime packages are used.
| Value | Use |
| - | - |
| `published` | Default. Use this for production traffic. |
| `unpublished` | Use this only to test unpublished runtime package changes. |
## Environment URLs
| Surface | Production |
| - | - |
| REST API | `https://api.webless.ai/api/v1` |
| Search WebSocket | `wss://api.webless.ai/api/v1/ws` |
| Agent Runtime | `https://runtime.webless.ai` |
| Data API | `https://portal.webless.ai` |
# Get completions
Source: https://docs.webless.ai/api-reference/latest/search/completions
api-reference/openapi-latest.json POST /api/v1/completions
Returns prefix-guaranteed inline autocomplete completions for query inputs. Use this for ghost text while a user is typing. Every returned `text` starts with the requested `prefix` case-insensitively.
Use this endpoint for inline query autocomplete while a user is typing. It is
separate from [`suggestions`](/api-reference/latest/search/suggestions), which
returns follow-up prompts after a query is submitted.
Call the endpoint after a short delay when the user stops typing, using the current
`prefix`. Abort in-flight requests when the prefix changes. Use the returned
`suffix` to render ghost text after the user's typed text.
Pass `previous_completions` when you want to avoid repeating completions already
shown to the user. When `is_private_session` is `true`, the API returns an empty
completion set.
# Select a CTA
Source: https://docs.webless.ai/api-reference/latest/search/cta-selection
api-reference/openapi-latest.json POST /api/v1/cta-selection
Returns the CTA text and destination URL for the same query context as the search response.
Call this after you create the `requestId` for the user action. The response
returns CTA text and a target URL for the same context.
# Generate FAQs
Source: https://docs.webless.ai/api-reference/latest/search/faqs
api-reference/openapi-latest.json POST /api/v1/faqs
Generate questions and answers from an indexed page.
Generate page-specific questions and answers from content already indexed for a
page. Use the page's canonical URL to identify the content.
Send URL context only. Do not send the full page body from the browser. The
API returns `not_found` when the page is not available in the selected index.
## Format the answers
| Goal | Field |
| - | - |
| Return between one and six questions | Set `max_items`. The default is `4`. |
| Return prose answers | Set `answer_format` to `paragraph`. Use `answer_paragraph_words` to target `50`, `100`, or `150` words. |
| Return list answers | Set `answer_format` to `bullets`. Use `answer_max_bullets` to return between one and six list items. |
| Adjust the writing style | Set `tone` to `neutral`, `warm`, `professional`, or `concise`. |
| Test unpublished runtime changes | Set `version` to `unpublished`. Production requests default to `published`. |
| Bypass the cache and summary analytics | Set `is_private_session` to `true` or `tracking_consent` to `false`. |
A bullet-formatted answer is one string containing newline-separated list
items. It is not an array.
## Handle the response status
The endpoint returns HTTP `200` for each application-level result.
| Status | Meaning |
| - | - |
| `ready` | `items` contains the generated question-and-answer pairs. |
| `not_found` | The page is not in the selected index. `items` is empty. |
| `error` | The API could not produce usable FAQs. |
# Health
Source: https://docs.webless.ai/api-reference/latest/search/health
api-reference/openapi-latest.json GET /api/v1/health
# Create query
Source: https://docs.webless.ai/api-reference/latest/search/query
api-reference/openapi-latest.json POST /api/v1/queries
Returns content tiles, highlights, a marketing message, and a CTA for the given query. The v1 route resolves company configuration from the runtime package for the selected version pointer.
Use the same `requestId` and `sessionId` for `queries`, the WebSocket summary
stream, `cta-selection`, and `suggestions` for a single user action.
# Generate a Snackable Read
Source: https://docs.webless.ai/api-reference/latest/search/snackable-read
api-reference/openapi-latest.json POST /api/v1/snackable-read
Generate concise takeaways from an indexed page.
Generate a short set of takeaways from content already indexed for a page. Use
the page's canonical URL to identify the content.
Send URL context only. Do not send the full page body from the browser. The
API returns `not_found` when the page is not available in the selected index.
## Choose generation behavior
| Goal | Field |
| - | - |
| Return between one and six takeaways | Set `max_bullets`. The default is `3`. |
| Adjust the writing style | Set `tone` to `neutral`, `warm`, `professional`, or `concise`. |
| Keep existing takeaways | Pass up to five strings in `existing_bullets`. They count toward `max_bullets`. |
| Test unpublished runtime changes | Set `version` to `unpublished`. Production requests default to `published`. |
| Bypass the cache and summary analytics | Set `is_private_session` to `true` or `tracking_consent` to `false`. |
In a private session, the API rewrites `existing_bullets` in the requested
tone. In a non-private session, it preserves them and generates only the
remaining bullets.
## Handle the response status
The endpoint returns HTTP `200` for each application-level result.
| Status | Meaning |
| - | - |
| `ready` | `bullets` contains the generated takeaways. |
| `not_found` | The page is not in the selected index. `bullets` is empty. |
| `error` | The API could not produce usable takeaways. |
The legacy `POST /api/v1/quick-read` route remains compatible. Use
`POST /api/v1/snackable-read` for new integrations.
# Get suggestions
Source: https://docs.webless.ai/api-reference/latest/search/suggestions
api-reference/openapi-latest.json POST /api/v1/suggestions
Returns follow-up suggestions related to the current query and recent session context.
Pass `previous_suggestions` when you want to avoid repeating prompts already
shown to the user.
# Create share
Source: https://docs.webless.ai/api-reference/latest/sessions/create-share
api-reference/openapi-latest.json POST /api/v1/shares
Creates a shareable conversation history snapshot for a session.
Private sessions cannot be shared. Requests with `is_private_session: true`
return `403`.
# Load shared history
Source: https://docs.webless.ai/api-reference/latest/sessions/load-share
api-reference/openapi-latest.json GET /api/v1/shares/{conversation_history_id}/sessions/{session_id}
Loads a shared history snapshot into a target session.
Shared history snapshots expire after 20 days.
# Create session summary
Source: https://docs.webless.ai/api-reference/latest/sessions/session-summary
api-reference/openapi-latest.json POST /api/v1/session-summaries
Generates a structured recap of a user's session for follow-up emails or internal dashboards.
This endpoint returns a generated summary directly unless the company has a
session summary webhook configured.
# Agent Studio and Agentic mode
Source: https://docs.webless.ai/guides/agent-studio
Enable Agentic mode, design agents in Agent Studio, publish to production, and install the hosted Agent or a custom UI.
Agentic mode replaces the classic search widget with a conversational Agent
experience. Configure agents in the Webless Console, publish from Setup, then
install with the production script or `@webless/agent`.
## How Agentic mode fits together
```mermaid theme={null}
flowchart LR
setup["Setup: Agentic mode"] --> publish["Publish to production"]
publish --> studio["Agent Studio"]
studio --> publishAgain["Publish updates"]
publish --> tag["Hosted Agent script"]
publish --> sdk["@webless/agent SDK"]
tag --> visitors["Live visitors"]
sdk --> visitors
```
| Mode | What visitors see | Install options in Setup |
| - | - | - |
| Agentic mode **off** | Classic Webless search widget | JavaScript, headless SDK, REST API, Google Tag Manager |
| Agentic mode **on** | Agent chat from Agent Studio | JavaScript, Agent SDK, and Agent Runtime API |
The REST API path in [Build a custom
frontend](/guides/custom-frontend) targets the **search widget** experience.
[Install the Webless tag](/guides/tag-installation) covers script placement
for the classic tag. When **published** production traffic uses Agentic mode,
install with the production script or `@webless/agent` instead.
## Enable Agentic mode
In the Webless Console, open your index and go to **Setup**.
In the **Agentic mode** card, enable the switch and choose **Save**.
On **Setup**, confirm your build is ready and choose **Publish to
production**.
Your first successful publish with Agentic mode saved unlocks **Agent
Studio** for that index. Publishing also binds the current Studio draft to
the production build visitors receive.
Open **Agent Studio** from the index navigation. Configure agents, design,
tools, and preview conversations.
Preview uses your latest saved Studio draft. Live traffic uses the
deployment from your most recent publish with Agentic mode on.
After you change agents or design in Studio, return to **Setup** and publish
again so production picks up the draft you want visitors to see.
## Install the hosted Agent
When Agentic mode is on, the **Install** section on Setup shows **JavaScript**
and **SDK** tabs.
Open the **JavaScript** tab and copy the production snippet shown for your
index.
Add it near the start of your site ``, the same way you would install
the classic tag. See [Install the Webless tag](/guides/tag-installation) for
placement and verification tips.
Deploy your site, open a production page, and confirm:
* The script loads without console errors.
* The Agent launcher or panel appears where you configured it in Agent Studio.
* A test question returns a streamed Agent reply.
## Build a custom Agent UI
Use `@webless/agent` when you want to own layout, branding, and surrounding
product chrome while still calling the Webless Agent runtime.
```bash theme={null}
npm i @webless/agent
```
Use the package from the browser only. Server-side rendering of the chat
surface is not required for a basic integration.
```javascript theme={null}
import { useAgentChat } from "@webless/agent/react";
export function AgentChat() {
const { state, submit } = useAgentChat({
indexId: "YOUR_INDEX_ID",
customerId: "YOUR_ORGANIZATION_ID",
runtimeOrigin: "https://runtime.webless.ai",
});
return (
{state.messages.map((message) => (
{message.text}
))}
{state.streamingText ?
{state.streamingText}
: null}
);
}
```
Copy exact `indexId`, `customerId`, and `runtimeOrigin` values from the **SDK**
tab in Setup after Agentic mode is enabled.
```javascript theme={null}
import { createAgentClient } from "@webless/agent";
const agent = createAgentClient({
indexId: "YOUR_INDEX_ID",
customerId: "YOUR_ORGANIZATION_ID",
runtimeOrigin: "https://runtime.webless.ai",
});
await agent.sendTurn("How does this work?", {
handlers: {
onDelta(text) {
// append streamed text
},
},
});
```
Publish Agentic mode to production before pointing real visitors at either the
hosted script or `@webless/agent`. Preview grants and unpublished drafts are
not a substitute for the live deployment.
## Switch back to the search widget
On **Setup**, turn off **Agentic mode**, save, and publish when prompted.
Setup shows the full install panel again: JavaScript, `@webless/headless`,
REST API, and Google Tag Manager for the classic search experience.
## Troubleshooting
Confirm Agentic mode is saved on Setup and that your workspace has access to
Agent Studio. You may need a successful publish with Agentic mode enabled
before Studio reflects production state.
With Agentic mode enabled, Setup keeps **JavaScript**, **SDK**, and **API**
for the Agent Runtime. **Google Tag Manager** still targets the classic
search widget. On **API**, follow the in-console steps or open
[Send a message](/api-reference/latest/agent/create-session) in the docs to
try bootstrap, session, and stream with your index ID.
Check that:
* `indexId` and `customerId` match the values in Setup.
* `runtimeOrigin` matches the environment Webless provisioned for your org.
* You published to production after your latest Agent Studio changes.
Preview exercises the unpublished Studio draft. Production serves the
deployment bound at publish time. Publish again from Setup after saving
Studio changes you want visitors to receive.
## Best practices
* Save Agent Studio changes before you publish; publishing materializes the
current draft.
* Re-test the production script after template, CSP, or consent-banner changes.
* Keep one integration path per environment: hosted script **or** custom SDK UI,
not both fighting for the same page real estate unless you design for it.
## Need help?
Contact the Webless team with:
* Your index ID and production site URL
* Whether Agentic mode is enabled and the time of your last publish
* The install path you use (hosted script or `@webless/agent`)
* Screenshots of browser console errors, if any
# Build a custom frontend
Source: https://docs.webless.ai/guides/custom-frontend
Use the REST endpoints and WebSocket streaming to power your own search UI.
Generate one `sessionId` per browser session and one `requestId` per user
action. Reuse both values across the coordinated calls for that action.
## Environments
| Environment | REST base URL | WebSocket URL |
| - | - | - |
| Production | `https://api.webless.ai` | `wss://api.webless.ai/api/v1/ws` |
Use `ws://` only for local development.
## Request flow
```mermaid theme={null}
sequenceDiagram
participant U as User
participant App as Your frontend
participant Search as POST /api/v1/queries
participant WS as WebSocket /api/v1/ws
participant CTA as POST /api/v1/cta-selection
participant Suggestions as POST /api/v1/suggestions
U->>App: Ask a question
App->>App: Reuse sessionId\nCreate requestId
par Fetch content tiles
App->>Search: POST /api/v1/queries
and Stream summary
App->>WS: get_summary_with_cache
and Fetch CTA
App->>CTA: POST /api/v1/cta-selection
and Fetch follow-up prompts
App->>Suggestions: POST /api/v1/suggestions
end
```
## API routes
| Use | Route |
| - | - |
| Query tiles | `POST /api/v1/queries` |
| Stream summary | `wss://api.webless.ai/api/v1/ws` |
| Select CTA | `POST /api/v1/cta-selection` |
| Get suggestions | `POST /api/v1/suggestions` |
## Orchestrate the full experience
Create a `sessionId` once per user session. Create a new `requestId` for each
query or action.
Call `POST /api/v1/queries` to fetch the top content tiles for the query.
Keep the same `sessionId` and `requestId` you will use everywhere else.
Open `/api/v1/ws` and send the `get_summary_with_cache` action. Append
`summary_part` chunks as they arrive, then finalize the UI when you receive
`summary_complete`.
Call `POST /api/v1/cta-selection` and `POST /api/v1/suggestions` with the
same IDs so the platform can correlate the entire experience.
## WebSocket request
```json theme={null}
{
"action": "get_summary_with_cache",
"params": {
"query": "What does Webless do?",
"company": "YOUR_COMPANY",
"requestId": "YOUR_REQUEST_ID",
"sessionId": "YOUR_SESSION_ID",
"version": "published",
"markdown": true,
"number_of_tiles": 6,
"summary_word_limit": 120
}
}
```
## WebSocket events
| Event | Meaning |
| - | - |
| `summary_part` | A streamed chunk of summary text |
| `summary_tiles` | Tile IDs emitted near completion |
| `summary_complete` | The stream finished successfully |
| `error` | The request failed |
## JavaScript example
```javascript theme={null}
const sessionId = getOrCreateSessionId();
const requestId = crypto.randomUUID();
const ws = new WebSocket("wss://api.webless.ai/api/v1/ws");
ws.onopen = () => {
ws.send(
JSON.stringify({
action: "get_summary_with_cache",
params: {
query: "What does Webless do?",
company: "YOUR_COMPANY",
requestId,
sessionId,
version: "published",
number_of_tiles: 6,
markdown: true,
summary_word_limit: 120
}
})
);
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.event === "summary_part") {
appendSummary(message.data.summary_part);
} else if (message.event === "summary_tiles") {
renderTileIds(message.data.summary_tiles);
} else if (message.event === "summary_complete") {
finalizeExperience();
} else if (message.event === "error") {
console.error(message.data?.message);
}
};
```
Use the pages in [API reference](/api-reference/latest/introduction) for
request and response schemas. Use this guide for orchestration and WebSocket
behavior.
# Embed forms in Webless
Source: https://docs.webless.ai/guides/embedded-forms
Send your existing form embed code to Webless and choose the right integration method.
JavaScript embed scripts are the recommended option. Use an iFrame only when a
JavaScript embed is not available.
## Choose an embed method
This is the default option for HubSpot, Marketo, Pardot, Typeform, and many
other form platforms.
* The form renders natively on the page
* Analytics and CRM tracking usually work as expected
* You usually only need to copy the platform's generated embed snippet
Use an iFrame when your platform does not offer a JavaScript embed or when
the JavaScript option is blocked.
```html theme={null}
```
UTM parameters and cookie-based attribution often work less reliably in an
iFrame.
API integrations provide the cleanest data model and the most control, but
they require implementation work on both sides. Treat this as a custom
project with Webless.
## Common platforms
**Where to find it**
* Go to **Marketing** -> **Forms**
* Open the form
* Click **Actions** -> **Share**
* Copy the full snippet from **Embed code**
**Example**
```html theme={null}
```
**Where to find it**
* Go to **Design Studio** -> **Forms**
* Open the form
* Click **Form Actions** -> **Embed Code**
**Example**
```html theme={null}
```
**Where to find it**
* Go to **Content** -> **Forms**
* Open the form
* Click **View Embed Code**
**Example**
```html theme={null}
```
**Where to find it**
* Open your WordPress admin
* Go to **Forms**
* Use the form ID shown next to the form name
**Example**
```text theme={null}
[gravityforms id="1" title="false" description="false"]
```
**Where to find it**
* Open the form
* Click **Share** -> **Embed in a web page**
**Example**
```html theme={null}
```
**Example**
```html theme={null}
```
**JavaScript**
```html theme={null}
```
**iFrame**
```html theme={null}
```
**Microsoft Forms**
```html theme={null}
```
**Google Forms**
```html theme={null}
```
**Where to find it**
* Go to **Assets** -> **Forms**
* Open the form
* Click **Actions** -> **View Form HTML**
**Example**
```html theme={null}
```
If you use a custom form builder, send Webless one of the following:
* A JavaScript embed snippet
* An iFrame embed snippet
* The hosted form URL
## What to send Webless
* The complete embed script or iFrame code
* The form platform name
* The form name
* Any campaign or tracking IDs you want associated with submissions
## FAQ
That is fine. Send the form URL to Webless and ask whether it can be
embedded through an iFrame.
Form platforms change their embed output over time. Copy the full code your
platform provides and send that version directly.
That is normal. `//` is a protocol-relative URL. Do not replace it with a
single slash.
# Listen to Webless analytics events
Source: https://docs.webless.ai/guides/event-listeners
Subscribe to Webless lifecycle events in the browser and forward them to your analytics stack.
Webless emits DOM custom events with the `webless:` prefix and a structured
`event.detail` payload.
## Add listeners after Webless initializes
```javascript theme={null}
const eventTypes = [
"session_start",
"searchbar_visible",
"searchbar_hidden",
"searchbar_close",
"searchbar_click",
"suggested_question_click",
"tile_click",
"click_cta",
"floating_button_visible",
"floating_button_click"
];
eventTypes.forEach((eventType) => {
document.addEventListener(`webless:${eventType}`, (event) => {
console.log(`[Webless Event: ${eventType}]`, event.detail);
// Forward to your analytics service here.
// analytics.track(eventType, event.detail);
});
});
```
## Supported events
| Event | Fired when | Core fields |
| - | - | - |
| `webless:session_start` | A new user session starts | `sessionId`, `timeStamp`, `eventType` |
| Event | Fired when | Core fields |
| - | - | - |
| `webless:searchbar_visible` | The search bar enters the viewport | `sessionId`, `location`, `timeStamp`, `eventType` |
| `webless:searchbar_hidden` | The search bar leaves the viewport | `sessionId`, `location`, `timeStamp`, `eventType` |
| `webless:searchbar_close` | A user closes the floating search bar | `sessionId`, `location`, `timeStamp`, `eventType` |
| Event | Fired when | Core fields |
| - | - | - |
| `webless:searchbar_click` | A user submits a search query | `query`, `sessionId`, `timeStamp`, `eventType` |
| `webless:suggested_question_click` | A user clicks a suggested question | `query`, `sessionId`, `timeStamp`, `eventType` |
| `webless:tile_click` | A user clicks a result tile | `query`, `sessionId`, `timeStamp`, `eventType` |
| `webless:click_cta` | A user clicks the CTA button | `query`, `sessionId`, `timeStamp`, `eventType` |
| Event | Fired when | Core fields |
| - | - | - |
| `webless:floating_button_visible` | The floating button enters the viewport | `sessionId`, `location`, `timeStamp`, `eventType` |
| `webless:floating_button_click` | A user clicks the floating button | `sessionId`, `location`, `timeStamp`, `eventType` |
## Payload shape
```typescript theme={null}
interface ClientEventPayload {
eventType: string;
timeStamp: number;
sessionId: string;
location?: "floating" | "nav";
query?: string;
[key: string]: unknown;
}
```
## Example payloads
```json session_start.json theme={null}
{
"sessionId": "abc123-def456-ghi789",
"timeStamp": 1703123456789,
"eventType": "session_start"
}
```
```json searchbar_click.json theme={null}
{
"query": "What is artificial intelligence?",
"sessionId": "abc123-def456-ghi789",
"timeStamp": 1703123456789,
"eventType": "searchbar_click"
}
```
```json floating_button_click.json theme={null}
{
"sessionId": "abc123-def456-ghi789",
"location": "floating",
"timeStamp": 1703123456789,
"eventType": "floating_button_click"
}
```
## Performance and troubleshooting
* Make sure Webless finished initializing before you add listeners.
* Make sure your listener script appears after the Webless script.
* Make sure no browser console errors stop later scripts from running.
Debounce frequent events before forwarding them to your analytics service.
```javascript theme={null}
const debounce = (fn, wait) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => fn(...args), wait);
};
};
const debouncedTrack = debounce((eventName, data) => {
analytics.track(eventName, data);
}, 1000);
```
# Export analytics data
Source: https://docs.webless.ai/guides/export-data
Retrieve a pre-signed download URL, wait for the file to be ready, and download the result.
Data files expire after 7 days. Download them as soon as the pre-signed URL is
ready.
## Export flow
Send a `GET` request to `https://portal.webless.ai/query-data` with:
* `company`
* `start_date`
* `end_date`
* `x-api-key` in the request headers
A successful request returns `202 Accepted` and a `presigned_url`. That URL
becomes valid once the export file is written.
Poll the `presigned_url` every 10 seconds with the `Range: bytes=0-0`
header. Stop polling when the response returns `200 OK` or
`206 Partial Content`.
Reuse the same pre-signed URL to download the completed file.
## Request parameters
| Parameter | Required | Format | Description |
| - | - | - | - |
| `company` | Yes | `string` | Your client code |
| `start_date` | Yes | `YYYY-MM-DD` | Start of the date range, inclusive |
| `end_date` | Yes | `YYYY-MM-DD` | End of the date range, inclusive |
## Example request
```http theme={null}
GET https://portal.webless.ai/query-data?company=webless&start_date=2025-07-01&end_date=2025-07-03
x-api-key: YOUR_API_KEY
```
## Example response
```json theme={null}
{
"presigned_url": "https://webless-client-data.s3.us-west-1.amazonaws.com/athena-results/.csv?X-Amz-..."
}
```
## Sample code
```python export_data.py theme={null}
import time
import requests
API_KEY = "YOUR_API_KEY"
API_URL = "https://portal.webless.ai/query-data"
response = requests.get(
API_URL,
params={
"company": "webless",
"start_date": "2025-07-01",
"end_date": "2025-07-03",
},
headers={"x-api-key": API_KEY},
timeout=30,
)
response.raise_for_status()
url = response.json()["presigned_url"]
while True:
probe = requests.get(url, headers={"Range": "bytes=0-0"})
if probe.status_code in (200, 206):
break
time.sleep(10)
download = requests.get(url, stream=True)
download.raise_for_status()
with open("webless_results.csv", "wb") as outfile:
for chunk in download.iter_content(8192):
outfile.write(chunk)
```
```javascript export-data.js theme={null}
const axios = require("axios");
const fs = require("fs");
const API_URL = "https://portal.webless.ai/query-data";
(async () => {
const trigger = await axios.get(API_URL, {
params: {
company: "webless",
start_date: "2025-07-01",
end_date: "2025-07-03"
},
headers: {
"x-api-key": "YOUR_API_KEY"
}
});
const url = trigger.data.presigned_url;
while (true) {
const probe = await axios.get(url, {
headers: { Range: "bytes=0-0" },
validateStatus: (status) => [200, 206, 403, 404].includes(status)
});
if ([200, 206].includes(probe.status)) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 10000));
}
const response = await axios.get(url, { responseType: "stream" });
response.data.pipe(fs.createWriteStream("webless_results.csv"));
})();
```
## Error handling
| Status | Meaning |
| - | - |
| `400` | Missing or invalid parameter |
| `403` | Invalid or missing API key |
| `404` | Incorrect endpoint |
| `429` | Rate limit exceeded |
| `500` | Server error |
For assistance, email [tech@webless.ai](mailto:tech@webless.ai). The source
guide states a business-day response target of 4 hours.
# Install the Webless tag
Source: https://docs.webless.ai/guides/tag-installation
Install the hosted Webless experience in your shared layout and verify it across your site.
## Standard install
Before you start, make sure you have:
* Access to your site's shared HTML template or layout file
* Your Webless script URL (please make sure to use published version)
```html theme={null}
```
Add the hosted script at the start of `` tag.
```html layout.html theme={null}
```
After you add the script:
1. Save your template files.
2. Deploy your website changes.
3. Open a page on your website.
Open your browser developer tools and confirm:
* The Webless script loads successfully
* No JavaScript errors appear in the console
* Webless content renders inside the container
## Complete example
```html theme={null}
Your website......
```
## Exclude specific pages
Remove the script from any template where Webless should not
render.
```html Pages without Webless theme={null}
```
## Troubleshooting
Check the basics first.
* Verify that the script URL is correct.
* Verify that the script tag is placed at the start of ``.
* Verify that your domain has been authorized by Webless.
## Best practices
* Load the script at the start of the body for faster rendering.
* Use HTTPS for every hosted asset.
* Re-test after major website releases or template changes.
## Need help?
If you run into issues, contact the Webless team with:
* Your website URL
* The script URL you deployed
* A screenshot of any console errors
* Edit access to your website, if you can provide it
# Webless documentation
Source: https://docs.webless.ai/index
Launch AI search, connected support, and data workflows across your website.
Use this site to launch Webless on your website, connect downstream tools, and
build custom search experiences when you need more control.
# Turn your website into an answer surface
Webless helps you add AI-powered search, guided discovery, analytics, and
support handoff flows without rebuilding your full site experience.
Install the standard Webless tag in your global template and verify it
across your site.
Coordinate REST calls and WebSocket streaming with a single request flow.
Pull analytics data with the Data API and download the final result file.
Route visitor conversations to Slack, Microsoft Teams, or Webex Contact
Center.
Flatten batched events into rows for enrichment and analysis.
Configure Okta SAML for your Webless workspace.
## What Webless connects
```mermaid theme={null}
flowchart LR
site["Your website"] --> launch["Webless tag or custom frontend"]
launch --> search["Search, answers, tiles, and CTAs"]
search --> analytics["Webless analytics events"]
analytics --> handoff["Human handoff"]
handoff --> slack["Slack"]
handoff --> teams["Microsoft Teams"]
handoff --> webex["Webex Contact Center"]
analytics --> clay["Clay webhook workflows"]
analytics --> export["Data export API"]
```
## Choose your path
Start with the hosted tag if you want the fastest rollout.
* Install the core experience with [Install the Webless tag](/guides/tag-installation).
* Pass existing lead capture into Webless with [Embed forms in Webless](/guides/embedded-forms).
* Track lifecycle events with [Listen to Webless analytics events](/guides/event-listeners).
Use the backend APIs when you want to own the UI and orchestration.
* Follow [Build a custom frontend](/guides/custom-frontend) for the complete request flow.
* Track lifecycle events with [Listen to Webless analytics events](/guides/event-listeners).
Connect Webless to the systems that help your team respond and report.
* Route conversations to a person with [Set up human handoff](/integrations/human-handoff).
* Push batched events into Clay with [Send events to Clay](/integrations/clay-webhooks).
* Configure SAML with [Set up SAML SSO with Okta](/integrations/okta-saml).
* Export historical data with [Export analytics data](/guides/export-data).
Use the exact script URL, company identifier, tag IDs, provider IDs, and API
keys that Webless provisions for your account. Several source guides use
example values.
# Send events to Clay
Source: https://docs.webless.ai/integrations/clay-webhooks
Create a Clay webhook table, flatten batched events, and enrich the results.
Webless sends batched event data to Clay. Clay then lets you flatten each
batch into rows and enrich the resulting records.
## Setup flow
In Clay:
* Click **New**
* Choose **Workbook**
* Search for **webhook**
* Select the webhook table source
Open **Edit source** or **Source**, find the webhook URL, and copy it.
Send the URL to Webless so we can start posting batched events.
After events arrive:
* Open an event row
* Find the `Data [...]` list in the cell details panel
* Click **Take action on list**
* Choose **Write each item to new row in other table**
Use these settings for the best default result:
* Destination: **New table**
* Method: **Send row for each item in a list**
* Enable **Auto-extract new columns**
* Enable **Auto-map existing columns** if you use an existing table
After the flattened table appears, add the fields you care about as
dedicated columns. Common fields include:
* Date
* Page
* Type
* Query
* Browser
* Company
* Summary
* Location
If you already know which attributes you want to report on, add those columns
first so your enrichment workflow stays focused and easy to scan.
# Set up human handoff
Source: https://docs.webless.ai/integrations/human-handoff
Connect Webless conversations to Slack, Microsoft Teams, or Webex Contact Center.
Human handoff lets a visitor ask for a person from the Webless widget. The
visitor stays in the widget. Your team replies from Slack, Microsoft Teams, or
Webex Contact Center.
Webless carries the transcript and page context into the provider, then sends
human replies back to the same visitor conversation.
## How it works
```mermaid theme={null}
flowchart LR
visitor["Website visitor"] --> widget["Webless widget"]
widget --> handoff["Webless handoff connection"]
handoff --> slack["Slack"]
handoff --> teams["Microsoft Teams"]
handoff --> webex["Webex Contact Center"]
slack --> widget
teams --> widget
webex --> widget
```
## Before you start
You need:
* Admin access to the Webless Console workspace.
* Permission to install or approve apps in the provider.
* A destination for new handoffs: a Slack channel, Teams channel, or Webex
Contact Center LiveChat target.
* One test visitor conversation to verify the full round trip.
| Provider | Destination | Admin usually needed |
| - | - | - |
| Slack | Workspace channel | Slack workspace admin or approved app installer |
| Microsoft Teams | Team channel | Microsoft Teams tenant admin |
| Webex Contact Center | LiveChat asset, domain, queue, or flow | Webex Contact Center admin |
## Configure Webless
Open your site or index in Webless Console, then select **Human Handoff**. Turn
on setup mode, choose where humans reply, and complete the provider steps.
Turn on **Human Handoff** for the widget. Visitors only see the human option
after a provider is connected.
Select **Slack**, **Microsoft Teams**, or **Webex Contact Center**.
Finish each setup step until Console shows the provider as connected.
Start a widget conversation, ask for a human, reply from the provider, and
confirm the visitor receives the reply in the widget.
## Slack
Use Slack when your team handles visitor conversations from a workspace channel.
### What you need
* A Slack workspace.
* Permission to install the Webless Slack app.
* A public channel, or a private channel where the Webless app has been invited.
### Setup
In **Human Handoff**, choose **Slack** and click **Connect Slack**.
Slack opens an authorization screen. Select the correct workspace and allow
the Webless app.
Pick the channel that should receive new handoff conversations. For private
channels, invite the Webless app to the channel first.
Console should show the Slack channel and mark the setup steps complete.
If your workspace restricts app installs, use **Invite admin** from Console so
the right Slack admin can finish authorization.
## Microsoft Teams
Use Microsoft Teams when agents or teammates work from a team channel. A Teams
tenant admin must install or approve the Webless app before the channel can
receive handoff requests.
### What you need
* Microsoft Teams tenant admin access.
* The target team and channel.
* The setup code shown in Webless Console.
### Setup
In **Human Handoff**, choose **Microsoft Teams** and click **Connect
Teams**. Webless generates a setup code and app package instructions.
In Teams admin center, upload or approve **Webless Handoff**, then add it to
the target team.
In the target channel, open **Agents and bots**. You should see **Webless
Handoff** in the app list.
In the target channel, mention the bot and send:
```text theme={null}
@Webless Handoff connect YOUR_SETUP_CODE
```
Replace `YOUR_SETUP_CODE` with the current code from Webless Console.
Trigger a handoff from the widget and reply from Teams. The reply should
appear in the visitor's Webless widget.
The setup code connects one Webless handoff configuration to one Teams
destination. Do not share it outside the admin or teammate completing setup.
## Webex Contact Center
Use Webex Contact Center when routing, business hours, agent assignment,
transfers, and closeout should stay in Webex.
Webless does not replace Contact Center routing. Webless creates the bridge from
the widget into Webex, then Webex owns the contact-center workflow.
### What you need
* Webex Contact Center admin access.
* Webex digital channels, Webex Connect, or Engage enabled for your tenant.
* A LiveChat asset for the website.
* A website domain added to that LiveChat asset.
* A Contact Center queue, entry point, team, or flow target for Webless
handoffs.
* Agents who can accept and reply to the queued task.
For production, your Webex admin authorizes the Webless service app. You do
not need to create a separate Webex developer service app unless Webless
support asks you to for a sandbox test.
### Setup
In **Human Handoff**, choose **Webex Contact Center** and authorize the
Webless service app from a Webex admin account.
Confirm the Webex tenant and region that should receive handoffs.
In Webex Contact Center, use the LiveChat asset that represents the website
where the Webless widget runs.
Add the exact website host to the LiveChat asset, for example
`example.com` or `support.example.com`. Do not include `https://` or a path.
Map the LiveChat asset to the queue, entry point, team, or flow that should
receive Webless handoffs.
Return to Console and select the Webex target. The setup steps should show
as complete.
The domain step is required. If the website domain is missing or does not match
the visitor page, Webex can reject the conversation before an agent ever sees it.
The Webless setup screen separates what Webless needs from what should remain in
Webex Contact Center.
## Test checklist
Before you turn handoff on for a production page, verify:
* The visitor sees the human handoff option in the Webless widget.
* The provider receives the handoff with transcript and page context.
* A human reply from the provider appears back in the widget.
* Follow-up visitor messages continue to reach the provider.
* Closing or resolving the provider conversation behaves as expected.
* The destination is owned by the right team.
## Troubleshooting
Confirm handoff is enabled in Webless Console and that the provider
connection is active.
Confirm the Webless Slack app is installed in the workspace. For private
channels, also confirm the app has been invited to the selected channel.
Make sure you selected the correct workspace and that your Slack account can
install apps. If app installs are restricted, invite a Slack workspace admin
from Console.
Ask a Teams admin to confirm **Webless Handoff** is uploaded, allowed, and
added to the target team. Then open **Agents and bots** in the channel and
search for **Webless Handoff**.
Make sure the message mentions `@Webless Handoff` and includes the current
setup code from Webless Console.
Confirm the LiveChat asset has the exact website domain, the Contact Center
target is mapped to a live queue or flow, and at least one agent can receive
that task type.
Open the LiveChat asset website settings and add the exact host where the
Webless widget runs. Use `example.com`, not `https://example.com/page`.
# Set up SAML SSO with Okta
Source: https://docs.webless.ai/integrations/okta-saml
Configure the Okta application and send the right metadata and group mappings to Webless.
Webless handles the Firebase SAML configuration. Your team only needs to
configure the Okta app and send the resulting IdP metadata and group mappings.
## Prerequisites
* Okta admin access
* Your SAML provider ID from Webless
* The ACS URL from Webless
## Configure Okta
Webless will provide:
* Your SAML provider ID, such as `saml.okta.client1`
* The ACS URL, such as `https://webless-frontend.firebaseapp.com/__/auth/handler`
In Okta, create a new **SAML 2.0** app integration and use the provider ID
in the app name so the app is easy to identify.
Use these values exactly.
| Field | Value |
| - | - |
| Single Sign On URL | `https://webless-frontend.firebaseapp.com/__/auth/handler` |
| Recipient URL | `https://webless-frontend.firebaseapp.com/__/auth/handler` |
| Audience Restriction | Your exact provider ID |
| Name ID Format | `EmailAddress` |
| Application username | `Email` |
Add the following attribute statements:
| Name | Format | Value |
| - | - | - |
| `email` | Unspecified | `user.email` |
| `firstName` | Unspecified | `user.firstName` |
| `lastName` | Unspecified | `user.lastName` |
Then add a group attribute statement so Okta emits the full group list:
| Name | Format | Filter |
| - | - | - |
| `groups` | Unspecified | `.*` |
Create and assign your admin and user groups to the SAML app. Common examples
include:
* Admin groups: `admin`, `administrator`, `team_lead`, `managers`
* User groups: `user`, `employee`, `staff`, `general`
Send all of the following to Webless:
* The Okta SSO URL
* The Entity ID or Issuer
* The full X.509 certificate
* The list of admin group names
* The list of user group names
* Your email domain
* Your authentication preferences for SAML, Google login, and password login
After Webless enables your domain, sign in with SSO and verify the expected
role assignment.
* Admin users should receive `{domain}_admin`
* Regular users should receive `{domain}_user`
## Troubleshooting
* Verify the group attribute statement exists
* Verify users are assigned to the correct Okta groups
* Verify those groups are assigned to the SAML application
* Verify the group names you gave Webless exactly match the group names in
Okta
* Verify the affected users belong to the expected groups
The Audience URI in Okta must exactly match the provider ID Webless gave
you, including case.
# Set up Webex Contact Center
Source: https://docs.webless.ai/integrations/webex-contact-center
Authorize Webless Human Handoff and route Webless widget visitors to Webex Contact Center agents.
Webless Human Handoff connects conversations from the Webless widget to Webex
Contact Center. When a visitor asks for a human, Webless sends the conversation
transcript and page context into Webex. Agents reply from Webex Contact Center,
and the visitor keeps chatting in the same Webless widget.
Webless creates the handoff and message bridge. Webex Contact Center still
owns routing, queues, business hours, agent assignment, transfers, and
closeout.
## Before you start
You need:
* An active Webless account with access to Webless Console.
* Webex Contact Center with digital channels enabled.
* A Webex Full Admin who can authorize the Webless service app.
* Webex Contact Center admin access to configure LiveChat or Web Chat routing.
* A website domain where the Webless widget is installed.
* At least one Webex Contact Center agent who can accept and reply to chat
tasks.
If you do not have a Webless account yet, contact Webless from the
[pricing page](https://www.webless.ai/pricing) or
[request a demo](https://www.webless.ai/contact-us). The Webless team will help
create your workspace and enable Human Handoff.
## Pricing and accounts
Webless pricing is managed through Webless. See
[Webless pricing](https://www.webless.ai/pricing) or contact your Webless
account team.
Webex Contact Center licensing, digital channel access, and any Webex usage
costs are managed by Cisco/Webex.
## Setup overview
```mermaid theme={null}
flowchart LR
admin["Webex Full Admin"] --> authorize["Authorize Webless service app"]
authorize --> webex["Configure Webex LiveChat target"]
webex --> console["Map target in Webless Console"]
console --> visitor["Visitor asks for a human"]
visitor --> agent["Agent replies from Webex"]
agent --> widget["Reply appears in Webless widget"]
```
## Configure Webex Contact Center
In Webex Contact Center, confirm your tenant has digital channels enabled
and can create or use a LiveChat or Web Chat asset.
Use the LiveChat asset that represents the website where the Webless widget
runs.
Add the exact website host to the LiveChat asset, for example
`example.com` or `support.example.com`. Do not include `https://` or a path.
Map the LiveChat asset to the queue, entry point, team, or flow that should
receive Webless handoffs.
Confirm at least one agent is assigned to the target queue or team and can
accept chat tasks in Webex Contact Center Agent Desktop.
The domain step is required. If the website domain is missing or does not match
the visitor page, Webex can reject the conversation before an agent sees it.
## Configure Webless
In Webless Console, open the site or index that owns the widget, then select
**Human Handoff**.
Select **Webex Contact Center** as the provider.
A Webex Full Admin authorizes the Webless service app. This allows Webless
to create and manage the Contact Center handoff for your configured target.
Confirm the Webex tenant and region that should receive handoffs.
Choose the LiveChat, queue, entry point, or flow target that should receive
Webless handoffs.
Save the configuration, start a visitor conversation from the Webless
widget, ask for a human, accept the task in Webex, and send a reply.
The Webless setup screen separates what Webless needs from what remains managed
inside Webex Contact Center.
## What Webless requests from Webex
Webless requests only the scopes needed to configure and operate the handoff
bridge:
| Scope | Purpose |
| - | - |
| `cjp:config` | Read and use Contact Center configuration needed for setup. |
| `cjp:config_read` | List Contact Center targets available to the authorized tenant. |
| `cjp:task_write` | Create and update Contact Center handoff tasks. |
| `cjp:task_read` | Read task status needed to keep the visitor experience in sync. |
| `spark:kms` | Support Webex platform authorization requirements for the service app. |
## Test checklist
Before enabling this for a production page, confirm:
* The visitor sees **Talk to a human** in the Webless widget.
* Webless creates a Webex Contact Center task.
* A Webex agent can accept the task.
* The transcript and page context are visible to the agent.
* A Webex agent reply appears back in the Webless widget.
* Follow-up visitor messages continue to reach Webex.
* Ending the conversation in Webex closes or resolves the handoff as expected.
## Troubleshooting
Confirm the Webex service app was authorized by a Webex Full Admin and that
the Webex tenant has Contact Center digital channels enabled.
Confirm the LiveChat asset has the exact website domain, the target is
mapped to a live queue or flow, and an agent is assigned and available for
chat tasks.
Check that the Webex queue or flow can route to an available agent. If no
agent is available or the Webex flow rejects the task, update the Webex
routing configuration and test again.
Use a Webex Full Admin account for the same tenant that owns the Contact
Center routing target. If the service app was changed after approval,
reauthorize it in Webex Control Hub.
## Support
For Webless setup help, contact [support@webless.ai](mailto:support@webless.ai).
For Webex Contact Center licensing, entitlements, or tenant provisioning, contact
your Cisco/Webex account team.
# Quickstart
Source: https://docs.webless.ai/quickstart
Go live with Webless in 30 minutes using the smallest set of decisions
## Launch checklist
Pick the smallest integration that fits your needs.
Use the hosted Webless script if you want Webless to render the experience
inside your site.
Use the backend APIs if you want to build your own UI around search,
summaries, suggestions, and CTAs.
Open your website and confirm the Webless experience appears where you
expect it.
* Check that the script loads successfully.
* Check that no console errors appear.
Expand the rollout only after the core experience works.
* Add [embedded forms](/guides/embedded-forms).
* Send [events to Clay](/integrations/clay-webhooks).
* Configure [Okta SAML](/integrations/okta-saml).
* Set up [data export](/guides/export-data).
Share the final information Webless needs to finish provisioning.
* Your production website URL
* Any Clay webhook URL
* Your Okta metadata and group mappings
* Any console errors or screenshots if something does not load
## Before you ask for help
* Capture the full script URL you deployed.
* Capture the page URL where the issue happens.
* Capture a screenshot of the browser console if an error appears.