Documentation

Everything you need to launch your chatbot

A plain-language guide to building, customizing, and embedding your Obdal AI chatbot — from your first App to going live on your website.

Overview

What Obdal is, in plain terms

Obdal turns your own content into a 24/7 AI chatbot you can place on any website. This page walks you through the whole journey. Before the steps, it helps to know five words you will see everywhere: an App is a single chatbot; its Knowledge base is the files and FAQs it answers from; the Widget is the chat bubble visitors see; the Designer is where you style that widget; and Conversations are the chats it handles. Once those click, the rest of the docs read quickly.

App (chatbot)

One chatbot is one App. Everything else — its knowledge, its design, its conversations — belongs to that App.

Knowledge base

The files you upload and the FAQs you write. Your chatbot answers only from this content, so it stays accurate to your business.

Widget

The chat bubble your visitors click to open a conversation. It lives on your website once you paste in the embed code.

Designer

The no-code studio where you style the widget — its colors, theme, position, and welcome message — with a live preview.

Conversations

Each chat your widget handles. Conversations are how usage is measured, and your plan sets how many you get each month.

How the pieces fit together

Your content powers an App, the App drives the Widget, and the Widget lives on your site.

  1. App
  2. Knowledge
  3. Widget
  4. Your site

One account can hold several Apps, and each one is fully separate — its own knowledge, its own API key, its own widget design, and its own conversations. Nothing leaks from one chatbot to another.

Getting started

From empty dashboard to live chatbot

You can have a working chatbot live in four steps. First, create an App from your dashboard. Second, add knowledge by uploading files or writing FAQs so the bot has something to answer from. Third, open the Designer and customize how the widget looks and where it sits. Fourth, publish by copying the embed code onto your site. Each step below links to its own deeper section, but this is the full happy path from empty dashboard to live chatbot.

  1. 1

    Create an App

    From your Apps dashboard, create a new App and give it a name. Naming it automatically generates its API key, so the chatbot is ready to be set up.

  2. 2

    Add your knowledge

    Upload files and write FAQs so the bot has something to answer from. Uploaded files are processed in the background and take a moment before they become searchable.

  3. 3

    Customize the widget

    Open the Designer to set the bot name, avatar, welcome message, theme, and colors. The live preview updates instantly as you change anything.

  4. 4

    Publish to your site

    Copy the embed code from the Designer's Publish tab and paste it onto your website. The Free plan is enough to go live.

Set your allowed origins, or the widget stays hidden

The widget only loads on the website addresses you list. If you skip this, the chat will not appear. See the Embedding section.

For non-English answers, set the language yourself

The bot does not guess the language. You set the widget's interface language in the embed code and tell the bot which language to reply in through the System Prompt. See the Multilingual chatbot section.
Embedding

Putting the widget on your website

Publishing means pasting one snippet of code onto your website. Open your App, go to the Designer's Publish tab, and copy the embed code. Paste it just before the closing tag of any page where the widget should appear — most site builders have a place for custom HTML or footer scripts. For the widget to load, your site's address must be added to the allowed origins, or the chat will stay hidden. If your site is a single-page app, the snippet still works once it is in your main layout. Direct WordPress and Shopify integrations are on the way; for now the embed code works on every platform.

The embed snippet

Copy this from the Designer's Publish tab — your real snippet carries your own API key.

<script>  window.obdalConfig = {    apiKey: "your-app-api-key",    baseUrl: "https://obdal.com"  };</script><script async src="https://obdal.com/widget-loader.js"></script>

Where to paste it

Add the snippet just before the closing </body> tag on every page where the widget should appear. It initializes itself — there is nothing else to wire up. Most site builders offer a custom HTML or footer-scripts area that is perfect for this.

Add your site to allowed origins

This is the number one reason a widget does not appear. The loader refuses to run on a domain you have not listed and quietly logs origin_not_allowed in the browser console. Add your exact site address in the Publish tab. When no origins are set, the Publish tab shows a warning so you do not miss it.

Single-page apps

If your site uses client-side routing, place the snippet once in your main layout. The loader initializes on page load and the widget stays available as visitors move between views.

Platform plugins

Dedicated WordPress and Shopify plugins are coming soon. Until they land, the script embed above works on every platform, including those two.

WordPressComing soon
ShopifyComing soon

It updates itself

The widget script updates automatically. You get new features and fixes without ever re-pasting the embed code.
Designer

Styling your widget, field by field

The Designer is where your chatbot gets its look, and a live preview updates as you change anything. It has four tabs. General covers the bot name, avatar, and welcome message. Appearance is the largest tab: theme and colors, header and message styles, fonts, window size, corner radius, animations, and the chat toggle button. Position sets which corner the bubble sits in, with separate placement for mobile. Publish gives you the embed code and the allowed-origins reminder. This section is a plain-language reference to every field, so you always know what each control does before you touch it.

Every change shows up immediately in the live preview, so you can see exactly what visitors will see before you publish.

The basics your visitors notice first.

FieldOptionsWhat it does
Bot NameTextThe name shown at the top of the chat window, such as your brand or assistant name.
AvatarSVG / PNG / JPGThe image beside the bot's messages. Upload an SVG, PNG, or JPG up to 1500px on its longest side.
Welcome MessageTextThe first message visitors see when they open the chat. Use it to set the tone and invite a question.
Colors and dark mode are explained in detail in the Theming section.
There is no language toggle in the Designer. The widget's interface language is set in the embed code — see the Multilingual chatbot section.
Multilingual

Running a chatbot in another language

Running a chatbot in another language takes two separate settings, and knowing the difference saves a lot of confusion. The widget's interface text (buttons, placeholders) ships in English and Hungarian; you choose which by setting the locale in your embed code — there is no toggle in the Designer for this. Separately, the language the bot replies in is controlled by your System Prompt, not by auto-detection: to get Hungarian answers, tell the bot to answer in Hungarian. One honest note up front: visitors cannot switch the chat language themselves, so you set the language for each embed rather than offering a picker.

Yes, the widget ships with Hungarian interface text and the bot can answer in Hungarian. It is just not a Designer toggle, which is why owners often assume it is not possible. There are two separate levers, and you set both yourself.

Lever 1 — Widget interface language

The widget's buttons and placeholders ship in English (the default) and Hungarian. You choose which by setting window.obdalConfig.locale in your embed code. Add the highlighted line below; the copied snippet does not include it, so this is a manual addition.

Set the widget locale

Add the locale line to your embed snippet — it is not there by default.

<script>  window.obdalConfig = {    apiKey: "your-app-api-key",    baseUrl: "https://obdal.com",    locale: "hu"  };</script><script async src="https://obdal.com/widget-loader.js"></script>

Lever 2 — The language the bot replies in

Which language the bot answers in is controlled by your System Prompt in App Settings, not by auto-detection. To get Hungarian answers, add an instruction such as "Always answer in Hungarian." The bot follows the prompt; it does not guess from the visitor's message.

A fully Hungarian chatbot

Set both levers together. Add locale: "hu" to the embed code so the interface is Hungarian, and add "Always answer in Hungarian" to the System Prompt so the replies are Hungarian. Set only one and you get a half-translated experience — for example a Hungarian interface but English answers.

There is no end-visitor language switcher

Visitors cannot change the chat language from inside the widget, and the bot does not auto-detect the language of a message. You decide the language for each embed. If you need both languages, run a separate embed for each one.

Supported interface languages: English (en, the default) and Hungarian (hu).

Running the chatbot in another language is available on every plan. It is never a paid upgrade.

Theming

Dark, light, and your brand colors

Obdal has two completely separate themes, and mixing them up is the most common point of confusion. The dashboard theme — the Light, Dark, or System toggle at the top right of your dashboard — only changes what you see while managing your chatbot. It has no effect on the widget. The widget theme is set in the Designer's Appearance tab under Theme & Color, where you pick Light or Dark and fine-tune six colors per mode: background, text, border, the user bubble, the bot bubble, and the primary accent. Your visitors see the widget theme you choose; they cannot switch it themselves.

Two separate themes

Changing one never changes the other. The dashboard theme is for you while you work; the widget theme is what visitors see on your site.

Dashboard theme

The Light / Dark / System toggle at the top right of your dashboard. Only affects what you see while managing your chatbot.

Widget theme

Set in the Designer's Appearance tab. This is what your visitors actually see on your website.

Light mode
Everything you need to launch your chatbot
The six colors for the light widget theme.
Dark mode
Everything you need to launch your chatbot
The six colors for the dark widget theme.

Theme and color in the Appearance tab

In the Designer's Appearance tab, pick a Light or Dark widget theme, then fine-tune six colors for that mode.

Background

The main surface color of the chat window.

Text

The color of message and label text.

Border

The color of dividers and the window edge.

User bubble

The background of messages sent by your visitor.

Bot bubble

The background of messages sent by the bot.

Primary

The accent color used for buttons and highlights.

Choose your brand color from six presets or set a custom one. The primary color you pick cascades to the user-bubble color, so the widget stays on brand without extra tweaking.

Visitors cannot switch the widget theme

Whatever theme you publish is what every visitor sees. There is no light/dark switch inside the widget for them. If your widget looks light but your dashboard is dark, that is expected — the two themes are independent.
Settings

App and profile settings

App Settings is the control panel for how your chatbot behaves, separate from how it looks. Here you manage the API key (you can regenerate it, but doing so breaks any embeds already using the old one), set the personality and tone, write the System Prompt that shapes every answer and its language, choose a currency, and turn on Live Chat for human handoff. Your own account details — name, email, and password — live separately in Profile Settings. This section explains each field and when you would change it.

App Settings

Per-chatbot configuration for how this App behaves.

FieldOptionsWhat it does
API KeyMasked · RegenerateAuthenticates your App. Shown with only the last few characters. You can regenerate it, but every existing embed using the old key will stop working.
Personality / ToneTone dropdownSets the voice the bot writes in, from a Tone Style dropdown — for example friendly, formal, or concise.
System PromptMax 2000 charsThe core instructions that shape every answer, including which language the bot replies in. The single most important setting for behavior.
Currency100+ searchableThe currency the bot uses when it talks about prices. Pick from a searchable list of over a hundred.
Live ChatOn / OffTurns on human handoff so a live agent can take over a conversation when a visitor needs a person.

Regenerating the API key breaks existing embeds

The embed code carries your API key. If you regenerate it, every snippet already on a website uses the old key and stops working. After regenerating, copy the fresh snippet from the Publish tab and re-paste it everywhere the widget appears.
The System Prompt also controls the language the bot replies in — see the Multilingual chatbot section.

Profile Settings

Your personal account details — name, email, and password — live in Profile Settings, separate from any App. Changing them does not affect your chatbots.

App Settings is about a chatbot; Profile Settings is about you. If you are hunting for a bot control under your account profile, look in App Settings instead.

FAQs

FAQs and your knowledge base

FAQs give your chatbot fast, exact answers to the questions you already know visitors ask, alongside the files in your knowledge base. You can create, edit, and delete FAQs, group them into categories, pin up to five so they surface first, reorder them, and switch any of them active or inactive. FAQs are part of every paid plan, so if you are on the Free plan you will upgrade to unlock them. This section covers managing your knowledge base and getting the most out of pinned, well-categorized answers.

Create, edit, and delete

Write a question and its answer, update them any time, and remove the ones you no longer need.

Group into categories

Organize FAQs into categories so related questions stay together and are easier to manage.

Pin up to five

Pin your most important answers — up to five — so they surface first for visitors.

Reorder freely

Arrange FAQs in the order you want, putting the most useful answers near the top.

Set active or inactive

Switch any FAQ on or off without deleting it, so you can retire an answer temporarily.

Create, edit, and delete
Group into categories
Pin up to five

FAQs are a paid-plan feature

FAQs are not available on the Free plan. They unlock from the Starter plan upward. Uploaded files still work on Free, so your chatbot can answer either way.

Use FAQs for short, frequently asked questions with exact answers, and uploaded files for longer reference material. Both feed the same chat, so the bot draws on whichever fits the question best.

Human handoff

Handing a chat to a real person

Sometimes a visitor needs a real person, and human handoff lets one step in. Turn on Live Chat in your App Settings, and your chatbot can pass a conversation to a live agent who takes over the chat. Each plan includes a set number of agent seats — the people on your side who can answer — scaling up as you move to larger plans. This section explains how to enable handoff, what happens during a takeover, and how seats map to your plan.

Turn on Live Chat

Open App Settings and switch on Live Chat (Human Handoff). Once it is on, an agent can step into any conversation and take over from the bot. You can also set an offline message for when no agent is available.

Agent seats scale with your plan

Each plan includes a number of agent seats — the teammates who can answer live chats. The settings card shows how many seats are used against your limit, and the count grows on larger plans. See the Plans section for the exact numbers.

When no agent is available

If no agent is online, visitors see the offline message you configured — for example an email address to reach you. The conversation is never left silent.

Turn on Live Chat
Sometimes a visitor needs a real person, and human handoff lets one step in. Turn on Live Chat in your App Settings, and your chatbot can pass a conversation to a live agent who takes over the chat. Each plan includes a set number of agent seats — the people on your side who can answer — scaling up as you move to larger plans. This section explains how to enable handoff, what happens during a takeover, and how seats map to your plan.
If no agent is online, visitors see the offline message you configured — for example an email address to reach you. The conversation is never left silent.

Human handoff is a paid-plan feature

Handoff is not available on the Free plan. It is included from the Starter plan upward, with more agent seats on larger plans.
Plans

Plans and what each one unlocks

Your plan decides what your chatbots can do, and this section maps each capability to where it unlocks. The limits that scale with your plan are the ones to watch: monthly conversations, storage for your files, how many widgets you can run, whether FAQs are available, human handoff and how many agent seats you get, removing the "Powered by" branding, the AI model tier, and your support and SLA level. The figures here come straight from your live plan data, so they are always current. When you are ready to compare side by side, head to the pricing page.

Free
Free
Monthly conversations
50
Knowledge storage
100 MB
Widgets
1
FAQs
Human handoff
Agent seats
0
Remove "Powered by" branding
AI model tier
Fast
Starter
15/mo
Monthly conversations
500
Knowledge storage
1 GB
Widgets
1
FAQs
Human handoff
Agent seats
2
Remove "Powered by" branding
AI model tier
Fast
Growth
Popular
39/mo
Monthly conversations
1,500
Knowledge storage
5 GB
Widgets
3
FAQs
Human handoff
Agent seats
5
Remove "Powered by" branding
AI model tier
Advanced
Pro
89/mo
Monthly conversations
4,000
Knowledge storage
20 GB
Widgets
10
FAQs
Human handoff
Agent seats
15
Remove "Powered by" branding
AI model tier
Premium
Business
179/mo
Monthly conversations
10,000
Knowledge storage
50 GB
Widgets
Unlimited
FAQs
Human handoff
Agent seats
Unlimited
Remove "Powered by" branding
AI model tier
Premium

These are available on every plan

API access, webhooks, running the chatbot in another language, and adding teammates are available to everyone — they are not locked behind a paid plan. Only the capabilities in the table above scale with your plan.
Developer API

The REST API for developers

The Developer API lets your own software do what the dashboard does: chat with the assistant, read conversations, upload knowledge files, push a product catalog that renders as product cards, and run live handoff. Every request is authenticated with two credentials that must belong to the same account: the app's API key from App Settings and the email you log in with. Keep both on your server. If you only need the widget on a website, skip this section; the embed code requires no coding.

Troubleshooting

Fixing the usual snags

Most issues come down to a handful of predictable causes, and each has a quick fix. If the widget still shows "Powered by Obdal," branding removal is a paid-plan feature — upgrade to hide it. If the bot answers in the wrong language, the fix is in your System Prompt, not a hidden language setting: tell it which language to reply in. And if the widget does not appear at all, the cause is almost always allowed origins — add your site's exact address in the Publish tab so the widget is permitted to load. This section walks through these and the other real confusions owners run into.

Why does it say "Powered by Obdal"?

Removing that badge is a paid-plan feature. It shows on the Free plan and is removable from the Starter plan upward. Upgrade and the branding disappears. See the Plans section.

See Plans

Why won't it answer in Hungarian?

The bot does not auto-detect the language. Set the reply language in your System Prompt (for example, "Always answer in Hungarian") and set the widget's interface language with the locale in your embed code. See the Multilingual chatbot section.

See Multilingual chatbot

The widget isn't appearing at all.

This is almost always allowed origins — the loader refuses to run on an unlisted domain and logs origin_not_allowed. Other blockers it logs include subscription_required, conversation_limit_reached, trial_limit_reached, owner_not_found, app_not_found, and a wrong API key. Check the Embedding section.

See Embedding

My dashboard is dark but the widget is light (or the reverse).

That is expected. The dashboard theme and the widget theme are independent settings, and changing one never changes the other. See the Theming section.

See Theming

I regenerated my API key and the widget broke.

The old key in your published embed no longer works. Copy the fresh snippet from the Publish tab and re-paste it everywhere the widget appears. See the App settings section.

See Settings

It is still blocked for another reason.

Open your browser's developer console on the page with the widget. The loader logs the exact reason it did not render, which points you straight to the fix.

See Embedding

Check the browser console first

When the widget does not behave, the browser's developer console is the fastest place to look. The loader prints a clear reason — such as origin_not_allowed — that tells you exactly what to fix.
Quickstart

Three steps to your first authenticated request.

The Developer API is a server-to-server surface. Every call carries your app's API key and your account email, so keep both on your backend and never ship them in browser code.

Copy your app API key

Open App Settings and copy the API key of the app you want to talk to. Regenerating the key invalidates the old one everywhere.

Send both credentials as headers

Add X-Api-Key with the key and X-Account-Email with the email you log in with. A mismatch on either returns 401 invalid_credentials.

Call the endpoints

Start with GET /developer/app to confirm the connection, then chat, upload knowledge files, push products, or manage live handoff.

curl example
curl -X POST https://obdal.com/api/v1/developer/chat \
  -H "X-Api-Key: $API_KEY" \
  -H "X-Account-Email: $ACCOUNT_EMAIL" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Do you ship to Canada?"
  }'
# {"success": true, "message": "...", "session_id": "..."}
JavaScript example
// Server-side only: the credentials must never reach the browser
const res = await fetch('https://obdal.com/api/v1/developer/chat', {
  method: 'POST',
  headers: {
    'X-Api-Key': process.env.OBDAL_API_KEY,
    'X-Account-Email': process.env.OBDAL_ACCOUNT_EMAIL,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ message: 'Do you ship to Canada?', session_id })
});
const { message, products } = await res.json();
Endpoint reference

Every Developer API endpoint, with its request and response.

All endpoints live under /api/v1/developer, require the two credential headers, and answer with JSON. Successful responses carry success: true; failures carry success: false, an error message and a machine-readable code.

Authentication on every request

Send X-Api-Key (or Authorization: Bearer <key>) together with X-Account-Email. Both must belong to the same app owner. Credentials are read from headers only; values in the query string or body are ignored so keys never land in access logs.

Connection

Confirm the credentials and discover what the app accepts.

GET

/api/v1/developer/app

JSON

Returns the app profile, plan limits and the accepted upload formats. Use it as a connection test.

Request

GET /api/v1/developer/app
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{
  "success": true,
  "app": { "id": 12, "name": "Support bot", "locale": "en", "human_handoff_enabled": true },
  "account": { "email": "you@example.com" },
  "limits": { "plan": { "name": "Growth" }, "usage": { "conversations": {...}, "storage": {...} } },
  "upload": { "extensions": [".pdf", ".txt", ".md", ".docx", ".xlsx", ".csv", ".html", ".json", ".jsonl", ".xml"], "max_size_kb": 10240 }
}

Chat, sessions and usage

Talk to the assistant from your own backend, read transcripts, close conversations and track token usage. Sessions started here are tagged with the api channel in your dashboard.

POST

/api/v1/developer/chat

JSON

Sends a visitor message and returns the assistant reply. Omit session_id to start a new conversation; pass stream: true to receive server-sent events. While the session is with a live agent the message is forwarded to the agent and the response is 202 with handoff_active: true.

Request

{
  "message": "Do you ship to Canada?",
  "session_id": "3f7c1a9e-6d2b-4c58-9c0e-7b1f2d3e4a55",
  "stream": false
}

Response

{
  "success": true,
  "message": "Yes, we ship to Canada within 5 business days.",
  "session_id": "3f7c1a9e-6d2b-4c58-9c0e-7b1f2d3e4a55",
  "products": [ { "type": "product", "title": "Alpine Snowboard", "url": "https://...", "price_min": "49.99", "currency": "USD" } ],
  "suggest_handoff": false
}
GET

/api/v1/developer/sessions

JSON

Lists conversations of the app, newest first, with pagination.

Request

GET /api/v1/developer/sessions?page=1&per_page=20
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{
  "success": true,
  "sessions": [
    { "session_id": "3f7c1a9e-...", "title": "Shipping to Canada", "last_at": "2026-09-13T10:30:00Z", "status": "active", "preview": "Yes, we ship..." }
  ],
  "meta": { "current_page": 1, "per_page": 20, "total": 1, "has_more": false }
}
GET

/api/v1/developer/sessions/{session_id}/messages

JSON

Returns the full transcript of a session together with its status and handoff state.

Request

GET /api/v1/developer/sessions/3f7c1a9e-.../messages?limit=100
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{
  "success": true,
  "session": { "session_id": "3f7c1a9e-...", "channel": "api", "status": "active", "is_closed": false, "handoff_status": "none", "message_count": 2 },
  "messages": [
    { "role": "user", "content": "Do you ship to Canada?", "sequence": 1, "timestamp": 1757757000000 },
    { "role": "assistant", "content": "Yes, we ship to Canada...", "sequence": 2, "timestamp": 1757757003000 }
  ]
}
POST

/api/v1/developer/sessions/{session_id}/close

JSON

Closes a session. Closed sessions reject new messages with 410.

Request

POST /api/v1/developer/sessions/3f7c1a9e-.../close
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{ "success": true, "closed": true }
GET

/api/v1/developer/usage

JSON

Aggregates requests, tokens and cost, optionally filtered by session_id, start_date and end_date.

Request

GET /api/v1/developer/usage?start_date=2026-09-01&end_date=2026-09-30
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{
  "success": true,
  "usage": { "total_requests": 1842, "total_input_tokens": 205401, "total_output_tokens": 216529, "total_tokens": 421930, "total_cost": 12.45 },
  "by_model": [ { "model": "gpt-5.6", "provider": "OpenAI", "requests": 1842, "total_tokens": 421930, "cost": 12.45 } ]
}

Knowledge files

Upload the same document types the dashboard accepts. Files are chunked, embedded and indexed in the background; poll the status until it reads completed.

POST

/api/v1/developer/files

multipart/form-data

Uploads up to 20 files per request as multipart/form-data under files[] (or a single file under file). Allowed types: pdf, txt, md, docx, xlsx, csv, html, json, jsonl, xml; 10 MB each.

Request

curl -X POST /api/v1/developer/files \
  -H "X-Api-Key: $API_KEY" -H "X-Account-Email: $EMAIL" \
  -F "files[]=@./faq.pdf" -F "files[]=@./returns-policy.md"

Response

{
  "success": true,
  "files": [
    { "id": 91, "name": "faq.pdf", "size": 48213, "mime_type": "application/pdf", "status": "pending", "stage": "queued", "progress": 0, "uploaded_at": "2026-09-13T10:31:00Z" }
  ]
}
GET

/api/v1/developer/files

JSON

Lists knowledge files with their processing status.

Request

GET /api/v1/developer/files
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{
  "success": true,
  "files": [ { "id": 91, "name": "faq.pdf", "status": "completed", "progress": 100, "chunk_count": 14, "indexed_at": "2026-09-13T10:32:10Z" } ]
}
GET

/api/v1/developer/files/{id}

JSON

Returns one file with its processing progress.

Request

GET /api/v1/developer/files/91
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{ "success": true, "file": { "id": 91, "name": "faq.pdf", "status": "completed", "progress": 100, "error": null } }
DELETE

/api/v1/developer/files/{id}

JSON

Removes a file, its chunks and its vectors from the knowledge base.

Request

DELETE /api/v1/developer/files/91
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{ "success": true, "deleted": true }

Products

Push your catalog so the assistant can answer product questions and show product cards in the chat, exactly like the Shopify integration. Product ids are yours: reuse them to update or delete.

POST

/api/v1/developer/products

JSON

Creates or updates up to 100 products in one call. Unchanged products are skipped; changed ones are re-indexed in place. Only id, title and an https url are required. Each product reports its own status (created, updated, skipped or failed), so one failing item never hides the others.

Request

{
  "products": [
    {
      "id": "sku-100",
      "title": "Alpine Snowboard",
      "url": "https://shop.example.com/products/alpine-snowboard",
      "description": "All-mountain board for powder and park.",
      "image_url": "https://cdn.example.com/alpine.jpg",
      "price": "49.99",
      "currency": "USD",
      "vendor": "Obdal",
      "product_type": "Snowboard",
      "in_stock": true,
      "collections": ["Winter Gear"],
      "tags": ["snow", "board"],
      "attributes": { "Length": "156 cm" },
      "variants": [ { "id": "v1", "title": "156 cm", "price": "49.99", "sku": "ALP-156", "available": true } ]
    }
  ]
}

Response

{
  "success": true,
  "summary": { "created": 1, "updated": 0, "skipped": 0, "failed": 0 },
  "results": [ { "id": "sku-100", "status": "created" } ]
}
GET

/api/v1/developer/products

JSON

Lists the products you pushed, with their indexing status.

Request

GET /api/v1/developer/products?page=1&per_page=50
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{
  "success": true,
  "products": [ { "id": "sku-100", "title": "Alpine Snowboard", "url": "https://...", "price_min": "49.99", "status": "completed", "updated_at": "2026-09-13T10:40:00Z" } ],
  "meta": { "current_page": 1, "per_page": 50, "total": 1, "has_more": false }
}
GET

/api/v1/developer/products/{id}

JSON

Returns one product by the id you assigned.

Request

GET /api/v1/developer/products/sku-100
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{ "success": true, "product": { "id": "sku-100", "title": "Alpine Snowboard", "in_stock": true, "variants": [...], "status": "completed" } }
DELETE

/api/v1/developer/products/{id}

JSON

Removes a product and its vectors.

Request

DELETE /api/v1/developer/products/sku-100
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{ "success": true, "deleted": true }

Human handoff

Manage live chat from your own tooling. The account owner acts as the agent: go online, watch the queue, claim a waiting conversation, reply, and end it. Send a heartbeat at least every 60 seconds to stay online.

GET

/api/v1/developer/handoff/settings

JSON

Shows whether live chat is enabled, the offline message, seat usage, and how many agents are online.

Request

GET /api/v1/developer/handoff/settings
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{
  "success": true,
  "settings": { "human_handoff_enabled": true, "handoff_offline_message": "Email help@example.com", "can_use_handoff": true, "handoff_available": true, "seats": { "limit": 3, "used": 1 }, "available_agents": 1, "my_availability": true }
}
PUT

/api/v1/developer/handoff/settings

JSON

Enables or disables live chat and sets the offline message. Requires a plan that includes human handoff.

Request

{ "human_handoff_enabled": true, "handoff_offline_message": "Email help@example.com" }

Response

{ "success": true, "settings": { "human_handoff_enabled": true, "handoff_offline_message": "Email help@example.com", ... } }
POST

/api/v1/developer/handoff/availability

JSON

Marks the account owner as available (or away) for live chats.

Request

{ "is_available": true }

Response

{ "success": true, "is_available": true, "ttl_seconds": 60 }
POST

/api/v1/developer/handoff/heartbeat

JSON

Keeps the owner's presence alive. Presence expires 60 seconds after the last heartbeat or availability call.

Request

POST /api/v1/developer/handoff/heartbeat
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{ "success": true, "is_available": true, "ttl_seconds": 60 }
GET

/api/v1/developer/handoff/queue

JSON

Lists conversations waiting for an agent, the ones you are handling, and recently ended ones.

Request

GET /api/v1/developer/handoff/queue
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{
  "success": true,
  "waiting": [ { "session_id": "3f7c1a9e-...", "preview": "I need a human", "requested_at": "2026-09-13T10:45:00Z" } ],
  "mine": [],
  "ended": [],
  "available_agents": 1
}
POST

/api/v1/developer/handoff/sessions/{session_id}/request

JSON

Escalates a session to a live agent. Returns requested when an agent is online, or no_agents with the offline message otherwise.

Request

POST /api/v1/developer/handoff/sessions/3f7c1a9e-.../request
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{ "success": true, "session_id": "3f7c1a9e-...", "handoff_status": "requested", "queue_timeout_seconds": 180 }
POST

/api/v1/developer/handoff/sessions/{session_id}/claim

JSON

Takes over a waiting conversation as the agent. Returns 409 not_claimable if it is no longer waiting.

Request

POST /api/v1/developer/handoff/sessions/3f7c1a9e-.../claim
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{ "success": true, "claimed": true, "handoff_status": "active" }
POST

/api/v1/developer/handoff/sessions/{session_id}/messages

JSON

Sends an agent reply into a conversation you have claimed.

Request

{ "message": "Hi, this is Anna. How can I help?" }

Response

{ "success": true, "message": { "id": 501, "role": "agent", "content": "Hi, this is Anna. How can I help?", "sequence": 7, "created_at": "2026-09-13T10:46:00Z" } }
GET

/api/v1/developer/handoff/sessions/{session_id}/messages

JSON

Polls the transcript; pass after_sequence to receive only newer messages.

Request

GET /api/v1/developer/handoff/sessions/3f7c1a9e-.../messages?after_sequence=7
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{
  "success": true,
  "handoff_status": "active",
  "agent": { "name": "Anna" },
  "ended_reason": null,
  "messages": [ { "role": "user", "content": "Where is my order?", "sequence": 8, "created_at": "2026-09-13T10:46:30Z" } ]
}
POST

/api/v1/developer/handoff/sessions/{session_id}/end

JSON

Ends the live chat and hands the conversation back to the assistant.

Request

POST /api/v1/developer/handoff/sessions/3f7c1a9e-.../end
X-Api-Key: your_app_api_key
X-Account-Email: you@example.com

Response

{ "success": true, "handoff_status": "ended" }

Errors and rate limits

Errors are JSON with success: false, a human-readable error and a stable code. Validation failures use the standard 422 shape with a message and an errors map.

StatusCodeMeaning
401invalid_credentialsThe API key or the account email is missing or does not match the app owner.
422validationA required field is missing or malformed; see the errors map.
403subscription_required / conversation_limit_reachedThe owner has no usable subscription (every endpoint), or the plan's conversation limit is reached (chat only).
403storage_limit_exceededThe upload would exceed the plan's storage limit. Product batches report this per item with status failed.
403handoff_unavailableLive chat is disabled for the app or not included in the plan. Escalate, claim and reply all re-check this.
409not_claimableThe session is not waiting for an agent.
409handoff_not_activeThe session is not currently assigned to you.
410session_closedThe session was closed and no longer accepts messages.
404—The session, file or product does not belong to this app.
429—Too many requests; wait for the Retry-After header.
Per API key and minute: 120 requests by default, 60 chat messages, 20 upload requests, 30 product batches. The limit headers X-RateLimit-Limit and X-RateLimit-Remaining are returned on every response.

Widget endpoints

The embed script talks to a separate set of public endpoints under /api/v1 that authenticate with the api_key alone and enforce your allowed origins. They exist for the widget; integrate your own software through the Developer API above.

Ready to Build Your AI Chatbot?

Upload your documents, embed the widget, and let customers get instant answers around the clock. Start free, and upgrade when your traffic grows.