The TypeScript SDK and headless mode
Developer·5 minutes to read
Verified against docs/superpowers/specs/2026-09-15-p4-b-typescript-sdk-headless.md §1.1, §1.3, §4.3, §4.4, §4.5; re-verified against the code at close-out.
What this does for you
You send a bot a task from a script or a build step and get its answer back on standard output.
Before you start
- An organisation API key of scope
writeor wider, created on the Developer page (Authentication and keys). - Node, and
@botseon/sdkinstalled in the project or available to the command. - Two environment variables:
BOTSEON_API_KEY, which is required, andBOTSEON_API_URL, which defaults to the loopback address a home install answers on. Neither value is ever printed, by the library or by the command. - The exact name, or the id, of the bot you are sending to. You can read both from Bots.
Steps
These steps run at a terminal and in a build log; the library has no screen of its own, so no screenshot is shown here. The one product screen a headless task touches is the conversation it appears in, unchanged: the task shows up as a message from the person the key belongs to, and the bot's answer follows as it would for anything typed in the app.
-
Put the key in the environment, never on the command line. A key on a command line is visible to every process and every log, and the command refuses a
--keyflag for exactly that reason. -
Point the command at your install with
BOTSEON_API_URL. It must be an absolutehttporhttpsURL, and plainhttpis admitted only for this machine. -
Send the task. The command's grammar is:
botseon-task --bot <id|name> [--wait | --no-wait] [--timeout <duration>] [--json] [--quiet] [--cancel-on-timeout] [--stop-on-approval] [--] <text> | --in place of the text reads it from standard input. -
Read the streams apart. Progress goes to standard error, one line per state change; the answer goes to standard output. A build log therefore shows the progress and a pipe carries only the answer.
-
Branch on the exit code rather than on the text:
0the run completed,1it did not,2the wait timed out,3the command refused before sending,4the service could not be reached,130the command was interrupted (Ctrl-CorSIGTERM). -
For a machine reader, add
--json. You then get exactly one line on standard output, whatever the outcome, carryingschemaVersion, the outcome, the run and conversation ids, the state, the text, the last result card, the spend in micro-cents, the steps, the elapsed milliseconds and the request id.
A worked example
Check that the key and the origin are right before a build step uses them:
curl -sS "$BOTSEON_API_ORIGIN/api/v1/bots?limit=5" \
-H "authorization: Bearer $BOTSEON_API_KEY" \
-H "accept: application/json"
The same work from the library, which is what the command is built on:
import { createClient } from '@botseon/sdk';
const client = createClient({
baseUrl: process.env.BOTSEON_API_ORIGIN ?? '',
apiKey: process.env.BOTSEON_API_KEY ?? '',
});
const result = await client.runTask({
bot: { name: 'Inbox Triage' },
text: 'Draft the release notes for v1.4.',
timeoutMs: 900_000,
onState: (run) => console.error(`run ${run.id} ${run.state}`),
});
if (result.outcome !== 'completed') process.exitCode = 1;
console.log(result.text);
runTask is three steps in one: it sends, it polls until the run settles, and it collects the bot's messages for that run into one piece of text. sendTask, waitForRun, cancelRun and resolveBot are on the same client when you want the steps apart.
What can go wrong
| What you see | Why | What to do |
|---|---|---|
BOTSEON_API_KEY is not set. | The variable is unset or empty in the environment the command inherited. | Export it in the same shell, or pass it into the build step's environment. |
--key is not accepted: a key on the command line is visible to every process and every log. Set BOTSEON_API_KEY instead. | A --key flag appeared anywhere in the arguments. | Move the key into the environment. |
BOTSEON_API_URL is not an absolute http or https URL. | The variable is set to something that is not a URL, or to a bare host. | Give it a scheme and a host. |
The API key was not accepted. Check BOTSEON_API_KEY and whether the key has expired or been revoked. | The key is unknown, revoked, expired, or its person is no longer an admin. | Check the key's row on the Developer page. |
No bot with that id or name is visible to this key. | The name matches nothing this key's person can see, or the id is not a real one. | List the bots first and copy the name exactly. |
More than one bot has that name. Use the id instead. | Two bots share the name you gave. | Send to the id; the command prints the candidates. |
The bot is still working on an earlier task. Try again when it has finished. | The bot had a run in flight for the whole queue timeout. | Raise --timeout, or wait and send again. |
The wait ran out before the run settled. | The run was still going when the timeout passed. | Read the run id from the message and follow it with Runs, or add --cancel-on-timeout. |
See also
Last verified against build c0f77aa.