Guide
Using it in a mobile app
Yes. The widget is a thin client over a plain HTTP API, and nothing in that API assumes a browser — no cookies, no redirects, JSON in and JSON out. An iOS or Android app talks to the same agent, on the same plan, with the same allowance. There is no separate mobile product to buy and no SDK to install.
What does change is how the app proves who it is, and that is worth getting right before you ship a binary someone can unpack.
Two ways in, and which one you want
If your app already has a backend, use it. Your server holds the site secret and mints a short-lived signed token; the app asks your server for one and sends it to us. The secret never ships inside the app, a token expires in minutes, and you decide which of your users gets a conversation. This is the path to take if you have the choice.
If your app has no backend, it can carry the public site key instead — the same key the website widget uses. Understand what that means: the key is extractable from any app binary, and unlike a website there is no domain to check it against. You have to turn this on deliberately, in the portal, under Also used from a mobile app on the site's Setup tab. Until you do, a request with no browser origin is refused.
In that case also tick Require each app install to register, directly below it. Your app then calls /v1/install once on first run and keeps the id it gets back, sending it on every request afterwards. Requests are rate-limited per registered install, so an extracted key does not give one caller an unrestricted path to the bot's shared allowance. It costs one extra call per installation.
The request sequence
Base URL https://kavilo.cloud. Send your credential as a header on every request — either Authorization: Bearer <token> or X-Kavilo-Key: kw_pub_….
POST /v1/install— once, on first run, if you ticked the setting above. Keep theinstallIdit returns and send it asX-Kavilo-Installfrom then on.POST /v1/session— open or resume. Send{"conversationId": "…"}, empty the first time. You get back the id to keep, a greeting, a cursor, and the history so far.POST /v1/message— send a turn. Answers202immediately; the reply arrives on whichever transport you are reading.POST /v1/poll— one of two ways to receive replies. Send your cursor and get whatever is new. Waits up to 25 seconds before answering empty, so a loop of these is a long-poll rather than a busy wait.GET /v1/stream— the alternative to polling: receive the same frames as server-sent events.POST /v1/end— the visitor is done. Sends the transcript and closes the conversation.
Poll and stream read the same buffer through the same cursor, so you can switch between them mid-conversation and lose nothing. On a phone, polling is usually the easier of the two: it survives a network change without special handling, and it does not hold a socket open while the user is reading.
Reading the reply
Each frame has a seq, a kind and usually some text. Append every token frame as it arrives. The turn ends on final, which carries the complete answer — take it as authoritative and replace what you accumulated, so a reconnect cannot leave you showing the reply twice. A turn can also end on error. Those two are the only endings; there is no separate "done" frame to wait for.
One message does not always mean one reply. If the visitor sends a second message while the agent is still answering, it is folded into the same turn. An app that blocks until it has counted one reply per message will eventually hang; read until final instead.
Backgrounding
A conversation that goes quiet for fifteen minutes is closed and its transcript sent — the same rule that applies on the web, where a visitor closing a tab looks exactly like one making tea. A phone that sleeps crosses that line easily.
So on returning to the foreground, call /v1/session again with the id you stored. If the conversation is still open you carry on; if it was closed you get a fresh one, and either way the response hands you the history to redraw. Treat that call as how the screen loads, not as error handling.
What is not there yet
Attachments. There is no upload endpoint on the chat API, for apps or for the web, so a visitor cannot send you a photo of the broken part. If that is what your app is for, tell us — it moves up the list when somebody actually needs it.
Start here
Ask us for the reference client and we will send the whole sequence in Swift and Kotlin — about a hundred lines each, and short enough to read rather than trust. Write to hello@kavilo.cloud and say which platform you are on.