BotSpot · developer docs · apps
An App is a static frontend with an agent as its backend. You build the page, bind it to one or more of your agents, and call them with a single function, botspot.run. This page covers what to send, what comes back, who pays, and what every error means.
Before you start
The page runs in the visitor's browser and is hosted as static files on your own address. It has no server of its own. Anything that needs a backend (reading data, calling a model, calling another service) is done by an agent, and the page reaches the agent through botspot.run(role, input).
A role is a name your page uses, such as assistant or scientist. In Workbench you bind each role to one of your agents. The page never holds an API key: the server resolves the role to the agent, runs it, and bills the right wallet.
So server routes, server actions and secrets inside the page are outside the model. If the page needs a secret or a third-party call, put it in the agent and let the agent do it.
Getting going
import { botspot } from './botspot.js';
// The agent bound to the role "scientist" has a Big Text input named "scientist"
// and a Big Text input named "question". Send them by name:
const res = await botspot.run('scientist', {
scientist: 'Marie Curie',
question: 'What did you learn from radium?',
});
res.output; // the reply, always a string
res.data; // the reply parsed as JSON, only when it is JSON
res.bics_charged; // what this run costIf you use the App builder, it is told each bound agent's inputs on every request, so it writes the call with the right keys. You can still read the contract yourself when you write the call by hand.
Reference
botspot.run(role, input) returns a promise for { ok, role, output, data?, bics_charged }.
| Field | Meaning |
|---|---|
| output | The agent’s reply, always a string. Agents write markdown, so render it as markdown rather than as plain text. |
| data | Present only when the reply is JSON (for example a workflow that ends in a data node). The SDK parses it once so your page does not have to guess keys. |
| bics_charged | What this run cost, when the platform could read it back. |
What you can pass as input.
// A plain string reaches ONLY a Chat Input.
await botspot.run('assistant', 'hello');
// is sent as { text: 'hello' }If a visitor is not signed in, botspot.run sends them to the hosted login and the page navigates away. It does not resolve in that case.
Reference
Every agent has a list of inputs it reads. A required input that the page does not send makes the run fail inside the workflow, which the page otherwise sees only as a failed request. The contract tells you the names up front.
GET /agent/v1/workflow/agents/{agent_id}/input-contract
{
"agent_id": "78f30519-…",
"inputs": [
{
"key": "scientist",
"node_id": "n_3",
"type": "input.bigtext",
"kind": "text",
"required": true,
"label": "Scientist",
"also_accepts_text": false
}
]
}| Field | Meaning |
|---|---|
| key | The name to send. It is the input node’s name, or its id if it has no name. |
| type, kind | The input node type, and a simple kind: text, files, image, audio, video or value. |
| required | Big Text inputs are required by default. Other inputs are optional unless the node says otherwise. |
| also_accepts_text | Chat inputs only: a plain string, or a top-level text key, also fills it. |
The contract is read from the same release that a run would use, so it cannot drift from what the agent actually does. It is limited to your own workspace's agents and needs an authenticated workspace call; a visitor's page cannot read it.
How the agent finds a value. Each input looks in this order: a nested entry under its node id, a nested entry under its name, then a top-level key by id, then by name. Sending by name is the simplest.
When it fails
A failed call throws an Error whose message is the cause. Show it.
try {
const { output } = await botspot.run('scientist', input);
show(output);
} catch (err) {
// err.message is the real cause. Show it. Do not replace it with sample text.
showError(err.message);
}| What you see | What it means | What to do |
|---|---|---|
| Insufficient BICS balance… | Status 402. The payer has no BICS left, or a preview link reached its spending limit. | Top up the wallet (published apps), or open the app from Workbench for a fresh preview (dev). |
| No agent is bound to role… | Status 404. The role name the page used has no binding. | Bind the role in Workbench, or fix the spelling in the page. |
| The agent needs … but the page didn’t send it | The agent’s workflow rejected the run because a required input was missing. Status 422 inside, shown as a 502 to the page. | Send the missing key. See the input contract. |
| Agent run failed (…) | The agent failed for another reason, or you are on a published app (see below). | Test the agent on its own in Workbench. |
| Sign-in redirect | Status 401. The session lapsed. | Nothing. The visitor signs in and returns. |
Development and published apps differ on purpose. On a development app the workspace is the one looking at the page, so the error carries the readable cause above. On a published app the person looking is a customer, so they get a plain status instead of your agent's internals.
In Workbench
When a botspot.run call from a development app fails, Workbench records it and shows a banner above the preview: what went wrong, what the page sent, and what was missing. Only key names are kept, never the values a user typed.
Who pays
| App state | Who pays | Who can run it |
|---|---|---|
| Development (unpublished) | The owning workspace’s organisation BICS pool. | Members of that workspace only. |
| Published | The signed-in visitor’s own prepaid wallet. | Any visitor with BICS. A zero balance is refused before the run starts. |
Published apps show a small BICS balance pill with a top-up popup, added for you. It is not shown on development hosts, because the balance there would be the wrong number.
Reference
| Call | Status |
|---|---|
| botspot.run(role, input) | Live. Described above. |
| botspot.auth.login / logout / isAuthenticated / token | Live. Login uses the hosted sign-in page. |
| botspot.wallet.balance / topup / open | Live. Used by the wallet pill. |
| botspot.files.upload | Placeholder. It returns a stub and does not store a file yet. |
| botspot.data.collection | Placeholder. It returns an empty list and does not store documents yet. |
Until the last two ship, keep files and records in an agent: a workflow with a data node reads and writes your connected database, and the page gets the rows back in data.
Where it runs
Troubleshooting
I gave it a value and it says the variable is missing.
You probably sent a string. A string becomes { text }, which only a Chat Input reads. Send an object keyed by the input's name.
My page shows an answer, but the agent never ran.
Look for a catch that fills in sample text. Remove it and show err.message.
One click made several requests.
That is a retry loop in the page code. Retries multiply the cost of a failing agent, so stop retrying after the first failure.
Where are the logs for an agent that failed?
In Workbench, open the agent's runs. For a development app the banner already names the missing input.
The builder chat changed my files and now they're gone, or a change got overwritten.
Every save keeps a snapshot — open the ⋯ menu above the preview and use Version history to restore an earlier one. See Version history in the App Builder guide.
Do other surfaces know an agent's inputs?
Not yet. Today the App builder reads the contract. Other surfaces will follow.