HTTP API server for controlling TJBot through node-tjbotlib.
This service initializes TJBot once at server startup, then exposes selected TJBot public methods as HTTP endpoints.
- Human-readable API spec: this README
- OpenAPI spec:
openapi.yaml
- Node.js 20+
node-tjbotlibavailable at../node-tjbotlib(this server depends ontjbotvia local file dependency)- TJBot hardware and
~/.tjbot/tjbot.tomlconfigured on the host
npm install
npm run devThe server starts on http://0.0.0.0:8765 by default.
You can override bind host/port:
HOST=127.0.0.1 PORT=9000 npm run devnpm install
npm run build
npm startcd /home/pi/.tjbot/tjbot-server
npm install
npm run buildCreate /etc/systemd/system/tjbot-server.service with:
[Unit]
Description=TJBot HTTP Server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=pi
Group=pi
WorkingDirectory=/home/pi/.tjbot/tjbot-server
Environment=NODE_ENV=production
Environment=HOST=0.0.0.0
Environment=PORT=8765
ExecStart=/usr/bin/npm start
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable tjbot-server
sudo systemctl start tjbot-serversudo systemctl status tjbot-server
sudo journalctl -u tjbot-server -fAfter code updates:
cd /home/pi/.tjbot/tjbot-server
npm install
npm run build
sudo systemctl restart tjbot-serverThe API intentionally excludes the following TJBot methods:
initialize()(done at server startup)setLogLevel()(configured in TJBot config)getRecipeConfig()(no active recipe context)look()(server-local file path is not useful to remote clients)sleep()(utility not needed for remote control)
- Base path:
/api/v1 - Content type:
application/jsonunless stated otherwise - Validation: request payloads are validated with AJV using JSON Schema-compatible constraints
- Error model: all non-2xx responses use a consistent
{ ok: false, error: { ... } }shape
{
"ok": true,
"data": {}
}{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "human-readable summary",
"details": []
}
}listen() is exposed in two forms:
POST /api/v1/listenfor offline, single-result transcriptionGET /api/v1/listen/streamfor streaming partial/final transcription via Server-Sent Events (SSE)
Only one streaming listen session may be active at a time.
GET /api/v1/status
POST /api/v1/listenGET /api/v1/listen/stream
POST /api/v1/seePOST /api/v1/vision/detect-objectsPOST /api/v1/vision/classify-imagePOST /api/v1/vision/detect-facesPOST /api/v1/vision/describe-image
POST /api/v1/shinePOST /api/v1/pulseGET /api/v1/shine/colorsGET /api/v1/shine/random
POST /api/v1/speakPOST /api/v1/play
POST /api/v1/wavePOST /api/v1/arm/backPOST /api/v1/arm/raisePOST /api/v1/arm/lower
Returns server and TJBot runtime state.
Example response:
{
"ok": true,
"data": {
"initialized": true,
"rpiModel": "Raspberry Pi 5",
"activeListenStream": false,
"lastKnown": {
"color": "#ff0000",
"speechText": "Hello from TJBot",
"audioFile": "/opt/sounds/chime.wav",
"armAction": "wave",
"updatedAt": "2026-05-22T20:30:00.000Z"
}
}
}Request:
{
"color": "red"
}Request:
{
"color": "#00ff00",
"duration": 1.0
}Returns all recognized named colors.
Returns one random recognized color.
Request:
{
"text": "Hello, I am TJBot"
}Request:
{
"soundFile": "/opt/tjbot/sounds/chime.wav"
}No request body.
No request body.
No request body.
No request body.
Offline single-result transcription.
Request body is optional and currently empty.
Response:
{
"ok": true,
"data": {
"transcript": "turn on the light"
}
}Streaming transcription using SSE.
Response header:
Content-Type: text/event-stream
Event stream format:
event: partial
data: {"text":"turn"}
event: partial
data: {"text":"turn on"}
event: final
data: {"text":"turn on the light"}
event: done
data: {}
Error event shape:
event: error
data: {"code":"LISTEN_FAILED","message":"..."}
Captures one image using TJBot camera.
Request:
{
"responseFormat": "base64"
}responseFormat values:
base64default: returns JSON withimageBase64andmimeTypebinary: returnsimage/jpegbytes directly
POST /api/v1/vision/detect-objectsPOST /api/v1/vision/classify-imagePOST /api/v1/vision/detect-facesPOST /api/v1/vision/describe-image
All vision analysis endpoints use the same image input contract:
{
"imagePath": "/tmp/frame.jpg"
}or
{
"imageBase64": "..."
}Exactly one of imagePath or imageBase64 must be provided.
- Schemas are designed to compile cleanly in AJV.
- Unknown properties are rejected (
additionalProperties: false). - Vision image payload enforces one-of semantics (
imagePathxorimageBase64).
The canonical machine-readable API definition is in openapi.yaml.