| Base URL | http://localhost:8085 |
| Content-Type | application/json |
| Authentication | None |
| CORS | Access-Control-Allow-Origin: * |
All endpoints support two output formats via ?format= query param or "format" in POST body.
| Format | Description |
|---|---|
toon |
Default. Compact CSV tables via TOON format. Requires pip install toons |
json |
Full nested data for programmatic access |
Uniform schema: every object in an array has identical keys in both formats. Missing values get defaults ("", 0). Booleans are 0/1 integers, not true/false.
All errors return JSON with an error field in "code: detail" format. Every error is designed to be actionable: it tells you what went wrong, echoes what you sent, and tells you what to do next.
{"error": "not_found: no entity with this id. ids are ephemeral, re-query buildings to get current ids", "id": -12345}
{"error": "invalid_type: not a floodgate. use buildings name:Floodgate to find floodgates", "id": -12345, "name": "LumberjackFlag"}
{"error": "invalid_param: speed must be 0-3 (0=pause, 1=normal, 2=fast, 3=fastest)", "got": 5}
{"error": "insufficient_science: not enough science points to unlock", "building": "LargePowerWheel", "scienceCost": 60, "currentPoints": 10}
{"error": "not_found: source district does not exist", "from": "Bad Name", "districts": ["District 1", "District 2"]}
{"error": "not_found: recipe does not exist for this building", "recipeId": "Bad", "available": ["PlankRecipe", "TreatedPlankRecipe"]}The error string starts with a machine-readable code. Parse the prefix before : to switch on it. Everything after : is human context.
| Code prefix | Meaning | Extra fields |
|---|---|---|
not_found |
Entity, building, or district does not exist | id, districts, available, building |
invalid_prefab |
Prefab name does not exist. Suggests similar names | prefab |
invalid_type |
Entity exists but wrong type for this operation | id, name |
invalid_param |
Parameter value out of range or invalid | got |
not_unlocked |
Building requires science unlock first | prefab, scienceCost, currentPoints |
insufficient_science |
Not enough science points | building, scienceCost, currentPoints |
no_population |
No beavers available to migrate | from, available, requested |
disabled |
Feature disabled in settings.json | |
unknown_endpoint |
Route not found | get_endpoints, post_endpoints |
invalid_body |
Malformed JSON request body | |
operation_failed |
Game service threw an exception | varies |
internal_error |
Unhandled server error |
All HTTP endpoints are accessible via the Python client:
timberbot.py <command> # TOON format
timberbot.py --json <command> # JSON format
timberbot.py <command> key:value # with parameters
timberbot.py --host=192.168.1.50 --port=8085 summary # remote connectionThe in-game Timberbot widget is the primary way to launch and configure the built-in agent. The Python CLI remains available for direct API access and advanced workflows.
List endpoints (buildings, beavers, trees, crops, gatherables, alerts, notifications) support server-side pagination via query params:
| Param | Default | Description |
|---|---|---|
limit |
100 | Max items to return. 0 = unlimited (all items) |
offset |
0 | Skip first N items |
When limit > 0, response wraps in a metadata object:
{"total": 200, "offset": 50, "limit": 10, "items": [...]}When limit=0, returns a flat array (backward compatible):
[{...}, {...}, ...]The Python CLI passes limit=0 by default (AI/scripts typically want all data).
List endpoints also support server-side filtering via query params:
| Param | Description |
|---|---|
name |
Case-insensitive substring match on entity name |
x |
X coordinate for proximity filter |
y |
Y coordinate for proximity filter |
radius |
Manhattan distance radius (requires x and y) |
Filters apply BEFORE pagination. The total in paginated responses reflects filtered count.
GET /api/buildings?name=Farm # all FarmHouses
GET /api/buildings?x=120&y=140&radius=20 # buildings near (120,140)
GET /api/trees?name=Pine&limit=10 # first 10 pine trees
GET /api/beavers?name=Bot&limit=0 # all bots (unlimited)
Health check. Answered on listener thread (works even when game is paused/loading).
CLI: timberbot.py ping
| Field | Type | Description |
|---|---|---|
| status | string | Always "ok" |
| ready | bool | Always true |
{"status": "ok", "ready": true}Full game state snapshot: settlement, time, weather, population, resources, trees (with species), crops (with species), housing, employment, wellbeing (with per-category breakdown), science, alerts, faction, DC location, building counts by role, and nearby tree/food clusters.
CLI: timberbot.py summary | timberbot.py --json summary
| Field | Type | Description |
|---|---|---|
| settlement | string | Save game / settlement name |
| faction | string | "Folktails" or "IronTeeth" |
| time | object | See GET /api/time. Includes speed (0-3) |
| weather | object | See GET /api/weather |
| districts | array | Per-district: population, resources, housing, employment, wellbeing, DC |
| districts[].resources | object | Flat totals per good: {"Water": 150, "Log": 80} |
| districts[].housing | object | {occupiedBeds, totalBeds, homeless} per district |
| districts[].employment | object | {assigned, vacancies, unemployed} per district |
| districts[].wellbeing | object | {average, miserable, critical} per district |
| districts[].dc | object | {x, y, z, orientation, entranceX, entranceY} district center location |
| trees | object | {markedGrown, markedSeedling, unmarkedGrown, species:[{name, markedGrown, unmarkedGrown, seedling}]} |
| crops | object | {ready, growing, species:[{name, ready, growing}]} |
| wellbeing | object | {average, miserable, critical, categories:[{group, current, max}]} (global) |
| science | int | Current science points |
| alerts | object | {unstaffed, unpowered, unreachable} counts |
| buildings | object | Building counts by role: {housing, wood, storage, power, food, water, ...} |
| treeClusters | array | Nearby tree clusters (within 40 tiles of DC, same z). Same format as tree_clusters |
| foodClusters | array | Nearby food clusters (within 40 tiles of DC, same z). Same format as food_clusters |
??? example "Example response"
```json
{
"settlement": "My Colony",
"faction": "IronTeeth",
"time": {"dayNumber": 42, "dayProgress": 0.65, "partialDayNumber": 42.65, "speed": 2},
"weather": {"cycle": 3, "cycleDay": 5, "isHazardous": false, "temperateWeatherDuration": 12, "hazardousWeatherDuration": 6, "cycleLengthInDays": 18},
"districts": [{"name": "District 1", "population": {"adults": 20, "children": 5, "bots": 2}, "resources": {"Water": 150, "Log": 80}, "housing": {"occupiedBeds": 25, "totalBeds": 30, "homeless": 0}, "employment": {"assigned": 18, "vacancies": 22, "unemployed": 2}, "wellbeing": {"average": 12.3, "miserable": 0, "critical": 1}, "dc": {"x": 120, "y": 140, "z": 2, "orientation": "south", "entranceX": 120, "entranceY": 139}}],
"trees": {"markedGrown": 5, "markedSeedling": 2, "unmarkedGrown": 120, "species": [{"name": "Pine", "markedGrown": 3, "unmarkedGrown": 80, "seedling": 12}]},
"crops": {"ready": 15, "growing": 30, "species": [{"name": "Kohlrabi", "ready": 10, "growing": 20}]},
"wellbeing": {"average": 12.3, "miserable": 0, "critical": 1, "categories": [{"group": "SocialLife", "current": 0.5, "max": 2.0}, {"group": "Fun", "current": 1.2, "max": 3.0}]},
"science": 450,
"alerts": {"unstaffed": 3, "unpowered": 1, "unreachable": 0},
"buildings": {"housing": 5, "wood": 3, "storage": 2, "power": 1, "food": 2, "water": 1},
"treeClusters": [{"x": 125, "y": 145, "z": 2, "grown": 15, "total": 22, "species": {"Pine": 12, "Birch": 5}}],
"foodClusters": [{"x": 115, "y": 135, "z": 2, "grown": 8, "total": 12, "species": {"BlueberryBush": 8, "Dandelion": 4}}]
}
```
Flat key-value pairs including settlement, faction, day, dayProgress, speed, cycle, cycleDay, isHazardous, tempDays, hazardDays, markedGrown, markedSeedling, unmarkedGrown, cropReady, cropGrowing, adults, children, bots, resource stocks (e.g. Water, Log), foodDays, waterDays, logDays, plankDays, gearDays, beds, homeless, workers, unemployed, wellbeing, miserable, critical, science, alerts, building role counts, treeClusters, foodClusters.
Built-in interactive agent control. The in-game widget is the primary surface for these actions, but the same shared agent is also available over HTTP.
Current built-in agent status.
| Field | Type | Description |
|---|---|---|
| status | string | idle, gatheringstate, interactive, done, or error |
| binary | string | Current agent binary, usually claude, codex, or a custom command |
| model | string | Current model name, or "" if default |
| goal | string | Current goal text |
| currentCmd | string | Internal progress text while gathering state, otherwise "" |
| lastError | string | Last launch/runtime error, if any |
{
"status": "interactive",
"binary": "codex",
"model": "gpt-5.4",
"goal": "reach 50 beavers with 77 well-being",
"currentCmd": "",
"lastError": ""
}Start the built-in interactive agent. This gathers fresh colony state via timberbot.py brain, generates a merged per-launch instructions file from skill/timberbot.md plus live colony state, and launches the selected binary interactively against that generated file.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| binary | string | no | claude |
CLI binary to launch |
| model | string | no | binary default | Model name passed via --model |
| effort | string | no | binary default | Reasoning effort passed via --effort |
| timeout | int | no | 120 |
Timeout in seconds for the brain gather step |
| goal | string | no | built-in default | Goal text appended to the startup message |
| command | string | no | null |
Custom launch template when binary is custom |
{
"binary": "codex",
"model": "gpt-5.4",
"effort": "medium",
"goal": "reach 50 beavers with 77 well-being"
}{"status": "started", "binary": "codex"}Possible errors:
agent_busyif the agent is already gathering state or running interactively
Stop the current built-in agent session.
{"status": "stopping"}Possible errors:
agent_not_runningif there is no active session to stop
Current day number and progress.
CLI: timberbot.py time
| Field | Type | Description |
|---|---|---|
| dayNumber | int | Current day number |
| dayProgress | float | Progress through current day (0.0-1.0) |
| partialDayNumber | float | Fractional day number |
{"dayNumber": 42, "dayProgress": 0.65, "partialDayNumber": 42.65}Current weather cycle and drought info.
CLI: timberbot.py weather
| Field | Type | Description |
|---|---|---|
| cycle | int | Current cycle number |
| cycleDay | int | Day within current cycle |
| isHazardous | bool | Currently in drought/badtide |
| temperateWeatherDuration | int | Days of temperate weather this cycle |
| hazardousWeatherDuration | int | Days of hazardous weather this cycle |
| cycleLengthInDays | int | Total cycle length |
{
"cycle": 3,
"cycleDay": 5,
"isHazardous": false,
"temperateWeatherDuration": 12,
"hazardousWeatherDuration": 6,
"cycleLengthInDays": 18
}Power networks. Groups all powered buildings by their connected network. Buildings sharing a power network have the same supply and demand values.
CLI: timberbot.py power
| Field | Type | Description |
|---|---|---|
| id | int | Network identity hash |
| supply | float | Current power supply (from generators with workers) |
| demand | float | Total power demand from consumers |
| buildings | array | Buildings on this network |
| buildings[].name | string | Building name |
| buildings[].id | int | Instance ID |
| buildings[].isGenerator | bool | Power source (wheel, engine) |
| buildings[].nominalOutput | float | Max power output capacity |
| buildings[].nominalInput | float | Power draw when running |
Networks with supply < demand are underpowered — powered buildings run intermittently. Isolated buildings (no power chain) appear as single-building networks with supply: 0.
Current game speed. Answered on listener thread (works when paused).
CLI: timberbot.py speed
| Field | Type | Description |
|---|---|---|
| speed | int | 0=pause, 1=normal, 2=fast, 3=fastest |
{"speed": 1}Set game speed.
CLI: timberbot.py set_speed speed:2
| Field | Type | Required | Description |
|---|---|---|---|
| speed | int | yes | 0=pause, 1=normal, 2=fast, 3=fastest |
{"speed": 2}{"speed": 2}{"error": "invalid_param: speed must be 0-3 (0=pause, 1=normal, 2=fast, 3=fastest)", "got": 5}Current work schedule.
CLI: timberbot.py workhours
| Field | Type | Description |
|---|---|---|
| endHours | float | Hour when work ends (1-24) |
| areWorkingHours | bool | Whether beavers are currently working |
{"endHours": 16.0, "areWorkingHours": true}Set when beavers stop working.
CLI: timberbot.py set_workhours end_hours:14
| Field | Type | Required | Description |
|---|---|---|---|
| endHours | int | yes | When work ends (1-24) |
{"endHours": 14}{"error": "invalid_param: endHours must be 1-24 (hour when work stops, default 18)", "got": 30}Game event history.
CLI: timberbot.py notifications
Array of notification objects.
| Field | Type | Description |
|---|---|---|
| subject | string | Event title |
| description | string | Event details |
| cycle | int | Cycle when event occurred |
| cycleDay | int | Day within cycle |
[
{"subject": "Drought ended", "description": "The drought is over.", "cycle": 2, "cycleDay": 18}
]Buildings with problems: unstaffed, unpowered, unreachable, or status messages.
CLI: timberbot.py alerts
Array of alert objects.
| Field | Type | Description |
|---|---|---|
| type | string | "unstaffed", "unpowered", "unreachable", or "status" |
| id | int | Building instance ID |
| name | string | Building name |
| workers | string | (unstaffed only) "assigned/desired" |
| status | string | (status only) Status description |
[
{"type": "unstaffed", "id": 12340, "name": "LumberjackFlag", "workers": "0/1"},
{"type": "unpowered", "id": 12350, "name": "Gristmill"},
{"type": "unreachable", "id": 12360, "name": "SmallWarehouse"}
]Beaver and bot counts per district.
CLI: timberbot.py population
Array of district population objects.
| Field | Type | Description |
|---|---|---|
| district | string | District name |
| adults | int | Adult beaver count |
| children | int | Child beaver count |
| bots | int | Mechanical beaver count |
[
{"district": "District 1", "adults": 20, "children": 5, "bots": 2}
]Resource stocks per district.
CLI: timberbot.py resources | timberbot.py --json resources
Flat array of {district, good, available, all} per resource with stock > 0.
[
{"district": "District 1", "good": "Water", "available": 150, "all": 200},
{"district": "District 1", "good": "Log", "available": 80, "all": 80}
]Keyed by district name, each containing resource objects with {available, all}.
{
"District 1": {
"Water": {"available": 150, "all": 200},
"Log": {"available": 80, "all": 80}
}
}Districts with population and resource data combined.
CLI: timberbot.py districts | timberbot.py --json districts
| Field | Type | Description |
|---|---|---|
| name | string | District name |
| population | object | {adults, children, bots} |
| resources | object | Keyed by good name: {available, all} |
[
{
"name": "District 1",
"population": {"adults": 20, "children": 5, "bots": 2},
"resources": {"Water": {"available": 150, "all": 200}, "Log": {"available": 80, "all": 80}}
}
]Flat rows with name, adults, children, bots, plus one key per resource with stock > 0.
Import/export settings per good per district.
CLI: timberbot.py distribution
| Field | Type | Description |
|---|---|---|
| district | string | District name |
| goods | array | Per-good settings |
| goods[].good | string | Good name |
| goods[].importOption | string | "None", "Allowed", or "Forced" |
| goods[].exportThreshold | float | Export when stock exceeds this |
[
{
"district": "District 1",
"goods": [
{"good": "Water", "importOption": "Allowed", "exportThreshold": 50.0},
{"good": "Log", "importOption": "None", "exportThreshold": 0.0}
]
}
]Set import/export for a specific good in a district.
CLI: timberbot.py set_distribution district:"District 1" good:Log import_option:Forced export_threshold:50
| Field | Type | Required | Description |
|---|---|---|---|
| district | string | yes | District name |
| good | string | yes | Good name (e.g. "Log") |
| import | string | no | "None", "Allowed", or "Forced" |
| exportThreshold | int | no | Export threshold (-1 to skip) |
{"district": "District 1", "good": "Log", "importOption": "Forced", "exportThreshold": 50}{"error": "not_found: district does not exist", "district": "Bad Name", "districts": ["District 1", "District 2"]}Move adult beavers between districts.
CLI: timberbot.py migrate from_district:"District 1" to_district:"District 2" count:3
| Field | Type | Required | Description |
|---|---|---|---|
| from | string | yes | Source district name |
| to | string | yes | Destination district name |
| count | int | yes | Number of adults to move |
{"from": "District 1", "to": "District 2", "migrated": 3}{"error": "not_found: source district does not exist", "from": "Bad Name", "districts": ["District 1", "District 2"]}{"error": "no_population: no beavers available to migrate in this district", "from": "District 1", "available": 0, "requested": 3}All placed buildings with state.
CLI: timberbot.py buildings | timberbot.py --json buildings
Supports server-side pagination (?limit=10&offset=20) and filtering (?name=Farm). See Pagination above.
Each building includes all applicable fields (absent fields mean the component doesn't exist on that building):
| Field | Type | Description |
|---|---|---|
| id | int | Unity instance ID (ephemeral per session) |
| name | string | Faction-qualified building name (for example FarmHouse.IronTeeth) |
| x, y, z | int | Origin coordinates |
| orientation | string | "south", "west", "north", or "east" |
| finished | bool | Construction complete |
| pausable | bool | Can be paused |
| paused | bool | Currently paused |
| floodgate | bool | Is a floodgate |
| height | float | (floodgate) Current gate height |
| maxHeight | float | (floodgate) Maximum gate height |
| constructionPriority | string | Priority while building |
| workplacePriority | string | Priority when finished |
| maxWorkers | int | Maximum worker slots |
| desiredWorkers | int | Desired worker count |
| assignedWorkers | int | Currently assigned workers |
| reachable | bool | Connected to district via paths |
| powered | bool | Has power (mechanical buildings) |
| isGenerator | bool | Generates power |
| isConsumer | bool | Consumes power |
| nominalPowerInput | int | Rated power input |
| nominalPowerOutput | int | Rated power output |
| powerDemand | int | Current grid power demand |
| powerSupply | int | Current grid power supply |
| buildProgress | float | Construction time progress (0-1) |
| materialProgress | float | Material delivery progress (0-1) |
| hasMaterials | bool | Has materials to resume |
| inventory | object | Goods in stock: {"Log": 5, "Plank": 3} |
| statuses | array | Active status messages |
| isWonder | bool | Is a monument/wonder |
| wonderActive | bool | Wonder is activated |
| dwellers | int | Current residents |
| maxDwellers | int | Max residents |
| isClutch | bool | Has a clutch mechanism |
| clutchEngaged | bool | Clutch is engaged |
| stock | int | Total items in all inventories |
| capacity | int | Total inventory capacity |
| storageMode | string | "accept", "obtain", "supply", or "empty" (storage buildings only, empty string otherwise) |
| allowedGood | string | Selected good for storage (empty if none selected) |
| recipes | array | Available recipe IDs for manufactories |
| currentRecipe | string | Active recipe ID (empty if none) |
| needsNutrients | bool | Breeding pod needs food delivered |
| nutrients | int | Breeding pod nutrient count |
| entranceX, entranceY, entranceZ | int | Entrance block on the building |
[
{
"id": 12340,
"name": "LumberjackFlag",
"x": 120, "y": 130, "z": 2,
"orientation": 0,
"finished": true,
"pausable": true,
"paused": false,
"workplacePriority": "Normal",
"maxWorkers": 1,
"desiredWorkers": 1,
"assignedWorkers": 1,
"reachable": true,
"entranceX": 120, "entranceY": 129, "entranceZ": 2
}
]Flat rows with: id, name, x, y, z, orientation, finished, paused, priority, workers (as "assigned/desired").
All trees (alive and dead).
CLI: timberbot.py trees
Supports server-side pagination (?limit=10) and filtering (?name=Pine). See Pagination above.
| Field | Type | Description |
|---|---|---|
| id | int | Instance ID |
| name | string | Tree species name |
| x, y, z | int | Coordinates |
| alive | bool | Not a dead stump |
| marked | bool | Marked for cutting |
| grown | bool | Fully grown |
| growth | float | Growth progress (0.0-1.0) |
[
{"id": 45600, "name": "Pine", "x": 115, "y": 140, "z": 2, "alive": true, "marked": false, "grown": true, "growth": 1.0}
]All crops (Kohlrabi, Soybean, Corn, etc) with growth status.
CLI: timberbot.py crops
Same fields as /api/trees. Filters to crop species only.
[
{"id": 78900, "name": "Kohlrabi", "x": 125, "y": 141, "z": 2, "alive": true, "marked": false, "grown": false, "growth": 0.45}
]Berry bushes and other gatherable resources.
CLI: timberbot.py gatherables
| Field | Type | Description |
|---|---|---|
| id | int | Instance ID |
| name | string | Resource name |
| x, y, z | int | Coordinates |
| alive | bool | Not dead |
[
{"id": 45700, "name": "BlueberryBush", "x": 110, "y": 135, "z": 2, "alive": true}
]All beavers and bots with wellbeing and needs.
CLI: timberbot.py beavers | timberbot.py --json beavers
Supports server-side pagination (?limit=10) and filtering (?name=Bot). See Pagination above.
| Mode | Description |
|---|---|
detail=basic (default) |
Active needs only, compact |
detail=full |
All needs (active + inactive) with group category field |
id=<id> |
Single beaver/bot by ID, all needs with group field |
CLI: timberbot.py beavers detail:full | timberbot.py beavers id:-12345
Bots always show all 3 needs (Energy, ControlTower, Grease) regardless of detail mode.
| Field | Type | Description |
|---|---|---|
| id | int | Instance ID |
| name | string | Beaver name |
| x, y, z | int | Grid position on the map |
| wellbeing | float | Wellbeing score |
| district | string | District name (e.g. "District 1") |
| needs | array | Per-need breakdown (see below) |
| needs[].id | string | Need name (Hunger, Thirst, Campfire, Scratcher, etc.) |
| needs[].points | float | Current points (0-1, higher = more satisfied) |
| needs[].wellbeing | int | Wellbeing contribution from this need |
| needs[].favorable | bool | Need is satisfied |
| needs[].critical | bool | Need is in critical state |
| needs[].group | string | Need category: BasicNeeds, Fun, Nutrition, Aesthetics, Awe, SocialLife, Boosts (detail=full only) |
| anyCritical | bool | Any need below warning threshold |
| lifeProgress | float | Age progress (0.0-1.0) |
| workplace | string | Assigned workplace name (empty if none) |
| isBot | bool | Mechanical beaver |
| carrying | string | (optional) Good being hauled (e.g. "Water", "Log") |
| carryAmount | int | (optional) Units being carried |
| liftingCapacity | int | Max carry capacity (detail=full only) |
| overburdened | bool | (optional, detail=full) Carrying heavy load, movement slowed |
| deterioration | float | (optional, bots only) Deterioration progress 0-1 (1 = fully degraded) |
| contaminated | bool | Contaminated by badwater |
| activity | string | Current status text (e.g. "Waiting for nutrients", "No available workers") |
| hasHome | bool | Has assigned dwelling |
Bot needs: Bots always show all 3 needs regardless of active state: Energy (charge from ChargingStation, drains 0.58/day), ControlTower (boost from ControlTower building, drains 72/day), Grease (from GreaseFactory, drains 0.2/day). Points range 0-1.
[
{
"id": 78900,
"name": "Bucky",
"wellbeing": 14.2,
"needs": [
{"id": "Hunger", "points": 0.8, "wellbeing": 1, "favorable": true, "critical": false},
{"id": "Thirst", "points": 0.6, "wellbeing": 1, "favorable": true, "critical": false}
],
"anyCritical": false,
"lifeProgress": 0.35,
"workplace": "LumberjackFlag",
"isBot": false,
"contaminated": false,
"hasHome": true
}
]Flat rows with: id, name, wellbeing, tier (ecstatic/happy/okay/unhappy/miserable), isBot, workplace, critical (need names joined with +).
Tier thresholds: >= 16 ecstatic, >= 12 happy, >= 8 okay, >= 4 unhappy, < 4 miserable.
All building templates with dimensions.
CLI: timberbot.py prefabs
| Field | Type | Description |
|---|---|---|
| name | string | Prefab name (e.g. "LumberjackFlag.IronTeeth") |
| sizeX | int | Width |
| sizeY | int | Depth |
| sizeZ | int | Height |
| scienceCost | int | Science points to unlock (omitted if 0) |
| unlocked | bool | Whether unlocked (omitted if no science cost) |
| cost | array | Material cost: [{"good": "Log", "amount": 2}] |
[
{
"name": "FarmHouse.IronTeeth", "sizeX": 2, "sizeY": 2, "sizeZ": 2,
"cost": [{"good": "Log", "amount": 4}, {"good": "Plank", "amount": 2}]
}
]Top 5 clusters of grown trees by density.
CLI: timberbot.py tree_clusters
| Field | Type | Description |
|---|---|---|
| x | int | Cluster center X |
| y | int | Cluster center Y |
| z | int | Cluster Z level |
| grown | int | Fully grown trees in cluster |
| total | int | Total trees in cluster |
| species | object | Count per species: {"Pine": 12, "Birch": 5} |
[
{"x": 125, "y": 145, "z": 2, "grown": 15, "total": 22, "species": {"Pine": 12, "Birch": 5}}
]Top 5 clusters of gatherable food (berries, bushes) by density. Excludes tree species.
CLI: timberbot.py food_clusters
| Field | Type | Description |
|---|---|---|
| x | int | Cluster center X |
| y | int | Cluster center Y |
| z | int | Cluster Z level |
| grown | int | Fully grown gatherables in cluster |
| total | int | Total gatherables in cluster |
| species | object | Count per species: {"BlueberryBush": 8, "Dandelion": 4} |
[
{"x": 115, "y": 135, "z": 2, "grown": 8, "total": 12, "species": {"BlueberryBush": 8, "Dandelion": 4}}
]Settlement name for the current save game. Answered on listener thread (works even when paused).
CLI: used internally by brain for per-settlement memory folders.
| Field | Type | Description |
|---|---|---|
| name | string | Settlement / save name |
{"name": "My Colony"}Terrain, water, occupants, and contamination for a rectangular region.
CLI: timberbot.py tiles x1:100 y1:100 x2:110 y2:110
| Field | Type | Required | Description |
|---|---|---|---|
| x1 | int | yes | Region min X |
| y1 | int | yes | Region min Y |
| x2 | int | yes | Region max X |
| y2 | int | yes | Region max Y |
| Field | Type | Description |
|---|---|---|
| mapSize | object | {x, y, z} total map dimensions |
| region | object | {x1, y1, x2, y2} clamped region |
| tiles | array | Per-tile data |
| tiles[].x, y | int | Tile coordinates |
| tiles[].terrain | int | Terrain height |
| tiles[].water | float | Water depth at tile |
| tiles[].badwater | float | (optional) Water contamination 0-1 |
| tiles[].occupants | array/string | (optional) json: [{name, z}, ...] array. toon: flat string "Path:2/Stairs:3" |
| tiles[].entrance | bool | (optional) Is an entrance tile |
| tiles[].seedling | bool | (optional) Has a seedling |
| tiles[].dead | bool | (optional) Dead tree stump (buildable) |
| tiles[].contaminated | bool | (optional) Soil contamination |
| tiles[].moist | bool | (optional) Irrigated soil |
{
"mapSize": {"x": 256, "y": 256, "z": 22},
"region": {"x1": 100, "y1": 100, "x2": 110, "y2": 110},
"tiles": [
{"x": 100, "y": 100, "terrain": 2, "water": 0.0},
{"x": 100, "y": 101, "terrain": 2, "water": 1.5, "badwater": 0.3},
{"x": 100, "y": 102, "terrain": 2, "water": 0.0, "occupants": [{"name": "Path", "z": 2}], "moist": true}
]
}Science points and unlockable buildings.
CLI: timberbot.py science
| Field | Type | Description |
|---|---|---|
| points | int | Current science points |
| unlockables | array | Buildings that cost science |
| unlockables[].name | string | Building prefab name |
| unlockables[].cost | int | Science cost |
| unlockables[].unlocked | bool | Already unlocked |
{
"points": 450,
"unlockables": [
{"name": "Gristmill.IronTeeth", "cost": 200, "unlocked": true},
{"name": "Engine.IronTeeth", "cost": 600, "unlocked": false}
]
}Population wellbeing breakdown by category. Shows current vs max score for each need group across all beavers.
CLI: timberbot.py wellbeing
| Field | Type | Description |
|---|---|---|
| beavers | int | Number of beavers counted |
| categories | array | Wellbeing categories |
| categories[].group | string | Category name (BasicNeeds, SocialLife, Fun, Nutrition, Aesthetics, Awe) |
| categories[].current | float | Average current wellbeing for this category |
| categories[].max | float | Average max possible wellbeing for this category |
| categories[].needs | array | Individual needs in this category |
| categories[].needs[].id | string | Need/building name |
| categories[].needs[].favorableWellbeing | float | Wellbeing bonus when satisfied |
| categories[].needs[].unfavorableWellbeing | float | Wellbeing penalty when unmet |
{
"beavers": 42,
"categories": [
{
"group": "SocialLife",
"current": 0,
"max": 2,
"needs": [
{"id": "Campfire", "favorableWellbeing": 1, "unfavorableWellbeing": 0},
{"id": "RooftopTerrace", "favorableWellbeing": 1, "unfavorableWellbeing": 0}
]
}
]
}Unlock a building using science points. Matches the exact UI flow (cost deduction + events + UI refresh).
CLI: timberbot.py unlock_building building:"Engine.IronTeeth"
| Field | Type | Required | Description |
|---|---|---|---|
| building | string | yes | Prefab name from GET /api/science |
{"building": "Engine.IronTeeth", "unlocked": true, "remaining": 250}{"building": "Engine.IronTeeth", "unlocked": true, "remaining": 450, "note": "already unlocked"}{"error": "insufficient_science: not enough science points to unlock", "building": "Engine.IronTeeth", "scienceCost": 600, "currentPoints": 450}{"error": "not_found: building not in toolbar. use prefabs to list all building names", "building": "BadName"}Pause or unpause a building.
CLI: timberbot.py pause_building id:12340 | timberbot.py unpause_building id:12340
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| paused | bool | yes | true to pause, false to unpause |
{"id": 12340, "name": "LumberjackFlag", "paused": true}{"error": "not_found: no entity with this id. ids are ephemeral, re-query buildings to get current ids", "id": 99999}{"error": "invalid_type: this building cannot be paused", "id": 12340, "name": "Levee"}Engage or disengage a clutch on a building.
CLI: timberbot.py set_clutch id:12340 engaged:true
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| engaged | bool | yes | true to engage, false to disengage |
{"id": 12340, "name": "GravityBattery", "engaged": true}Remove a building from the world.
CLI: timberbot.py demolish_building id:12340
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
{"id": 12340, "name": "LumberjackFlag", "demolished": true}{"error": "not_found: no entity with this id, ids are ephemeral so re-query buildings or crops", "id": 99999}Remove a planted crop entity from the world.
CLI: timberbot.py demolish_crop id:12340
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Crop instance ID |
{"id": 12340, "name": "Kohlrabi", "demolished": true}{"error": "not_found: no entity with this id, ids are ephemeral so re-query buildings or crops", "id": 99999}Place a building in the world. Validates all tiles before placing: occupancy, terrain height, water, unlock status, underground clipping. Coordinates refer to the bottom-left corner regardless of orientation.
CLI: timberbot.py place_building prefab:Path x:120 y:130 z:2 orientation:south
| Field | Type | Required | Description |
|---|---|---|---|
| prefab | string | yes | Prefab name from GET /api/prefabs |
| x | int | yes | Bottom-left X |
| y | int | yes | Bottom-left Y |
| z | int | yes | Terrain height (must match) |
| orientation | string | yes | south, west, north, east |
!!! danger "Ghost buildings" Failed placements may create invisible entities. See Known Issues.
{"id": 12340, "name": "LumberjackFlag", "x": 120, "y": 130, "z": 2, "orientation": 0}{"error": "invalid_prefab: 'BadName' not found. Similar: LumberjackFlag.IronTeeth, LumberjackFlag.Folktails", "prefab": "BadName"}{"error": "not_unlocked: use science/unlock first", "x": 120, "y": 130, "z": 2, "prefab": "Engine.IronTeeth", "scienceCost": 600, "currentPoints": 450}{"error": "occupied by Path at (120,130,2). demolish it or try a different location", "x": 120, "y": 130, "z": 2, "prefab": "LumberjackFlag.IronTeeth"}Find valid placements for a building within a rectangular area. Returns at most 10 results. Water buildings sort by waterDepth first (deepest water preferred). Others sort by: non-flooded > reachable > pathAccess > nearPower.
CLI: timberbot.py find_placement prefab:LumberjackFlag.IronTeeth x1:110 y1:125 x2:130 y2:145
| Field | Type | Required | Description |
|---|---|---|---|
| prefab | string | yes | Prefab name |
| x1 | int | yes | Search area min X |
| y1 | int | yes | Search area min Y |
| x2 | int | yes | Search area max X |
| y2 | int | yes | Search area max Y |
| Field | Type | Description |
|---|---|---|
| prefab | string | Requested prefab |
| sizeX | int | Building width |
| sizeY | int | Building depth |
| placements | array | Valid positions (max 10) |
| placements[].x, y, z | int | Bottom-left coordinates |
| placements[].orientation | string | Best orientation name |
| placements[].entranceX | int | Doorstep tile X (where path must connect) |
| placements[].entranceY | int | Doorstep tile Y (where path must connect) |
| placements[].pathAccess | int | 1 if doorstep tile has a path, 0 otherwise |
| placements[].reachable | int | 1 if connected to district road network, 0 otherwise |
| placements[].nearPower | int | 1 if adjacent to power building, 0 otherwise |
| placements[].flooded | int | 1 if water on ground tiles. Flooded buildings are non-functional |
| placements[].waterDepth | float | Water depth at intake tile (water buildings only) |
| placements[].distance | float | Path distance from DC entrance via flow field (-1 if unreachable, lower = closer) |
Water buildings (pumps) sort by: waterDepth (deepest first). Others sort by: non-flooded > reachable > distance (closer first) > pathAccess > nearPower.
JSON format (nested wrapper with prefab metadata):
{
"prefab": "LumberjackFlag.IronTeeth",
"sizeX": 2, "sizeY": 2,
"placements": [
{"x": 120, "y": 130, "z": 2, "orientation": "south", "entranceX": 120, "entranceY": 129, "pathAccess": 1, "reachable": 1, "distance": 12.0, "nearPower": 0, "flooded": 0}
]
}TOON format (flat array, same keys, no wrapper):
[{"x": 120, "y": 130, "z": 2, "orientation": "south", "entranceX": 120, "entranceY": 129, "pathAccess": 1, "reachable": 1, "distance": 12.0, "nearPower": 0, "flooded": 0}]{"error": "invalid_prefab: 'BadName' not found. Similar: LumberjackFlag.IronTeeth, LumberjackFlag.Folktails", "prefab": "BadName"}Set floodgate water gate height. Value is clamped to max.
CLI: timberbot.py set_floodgate id:12340 height:1.5
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| height | float | yes | Desired gate height (clamped to 0-max) |
{"id": 12340, "name": "Floodgate", "height": 1.5, "maxHeight": 3.0}{"error": "invalid_type: not a floodgate. use buildings name:Floodgate to find floodgates", "id": 12340, "name": "LumberjackFlag"}Set construction or workplace priority.
CLI: timberbot.py set_priority id:12340 priority:VeryHigh
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| priority | string | yes | "VeryLow", "Normal", or "VeryHigh" |
| type | string | no | "construction" or "workplace". If empty, sets whichever exists |
{"id": 12340, "name": "LumberjackFlag", "workplacePriority": "VeryHigh"}{"id": 12340, "name": "LumberjackFlag", "constructionPriority": "VeryHigh"}{"error": "invalid_param: priority must be one of: VeryLow, Low, Normal, High, VeryHigh", "got": "Bad"}Set desired worker count for a workplace.
CLI: timberbot.py set_workers id:12340 count:2
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| count | int | yes | Desired workers (clamped to 0-maxWorkers) |
{"id": 12340, "name": "LumberjackFlag", "desiredWorkers": 2, "maxWorkers": 3, "assignedWorkers": 1}{"error": "invalid_type: not a workplace. only staffed buildings (lumberjacks, farms, etc) have workers", "id": 12340, "name": "Path"}Prioritize hauling deliveries to a building.
CLI: timberbot.py set_haul_priority id:12340 prioritized:true
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| prioritized | bool | yes | true to prioritize, false to clear |
{"id": 12340, "name": "SmallWarehouse", "haulPrioritized": true}{"error": "invalid_type: no haul priority. only buildings with inventories support haul priority", "id": 12340, "name": "Path"}Set which recipe a manufactory produces.
CLI: timberbot.py set_recipe id:12340 recipe:PlankRecipe
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| recipe | string | yes | Recipe ID, or "none" to clear |
{"id": 12340, "name": "LumberMill", "recipe": "PlankRecipe"}{"id": 12340, "name": "LumberMill", "recipe": "none"}{"error": "not_found: recipe does not exist for this building", "recipeId": "BadRecipe", "available": ["PlankRecipe", "TreatedPlankRecipe"]}Prioritize planting or harvesting for a farmhouse.
CLI: timberbot.py set_farmhouse_action id:12340 action:planting
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| action | string | yes | "planting" or "harvesting" (harvesting = default behavior) |
{"id": 12340, "name": "FarmHouse", "action": "planting"}{"id": 12340, "name": "FarmHouse", "action": "default"}{"error": "invalid_type: not a farmhouse. use buildings name:FarmHouse to find farmhouses", "id": 12340, "name": "LumberjackFlag"}{"error": "invalid_param: action must be 'planting' or 'harvesting'", "got": "bad"}Prioritize which tree/resource type a forester plants.
CLI: timberbot.py set_plantable_priority id:12340 plantable:Pine
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| plantable | string | yes | Plantable template name, or "none" to clear |
{"id": 12340, "name": "Forester", "prioritized": "Pine"}{"id": 12340, "name": "Forester", "prioritized": "none"}{"error": "not_found: plantable not in this building's list", "plantableName": "BadTree", "available": ["Pine", "Birch", "Oak"]}Set storage mode and/or allowed good on any storage building (piles, warehouses, tanks). Set either or both fields in one call.
CLI: timberbot.py set_storage id:12340 good:Water mode:obtain
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building instance ID |
| good | string | no | Good name (e.g. "Water", "Plank") or "none" to clear |
| mode | string | no | "accept" (default), "obtain", "supply", or "empty" |
Modes:
accept: normal behavior, haulers bring/take goods naturallyobtain: haulers actively fetch this good from other stockpilessupply: haulers actively take goods from here to other stockpilesempty: haulers remove all goods, building stops accepting
{"id": 12340, "name": "SmallTank.IronTeeth", "good": "Water", "mode": "obtain"}{"error": "invalid_type: not a storage building. piles, warehouses, and tanks have storage settings", "id": 12340, "name": "LumberjackFlag"}{"error": "invalid_param: mode must be accept, obtain, supply, or empty", "got": "badvalue"}Mark or clear a rectangular area for tree cutting.
CLI: timberbot.py mark_trees x1:110 y1:130 x2:120 y2:140 z:2 | timberbot.py clear_trees x1:110 y1:130 x2:120 y2:140 z:2
| Field | Type | Required | Description |
|---|---|---|---|
| x1 | int | yes | Area min X |
| y1 | int | yes | Area min Y |
| x2 | int | yes | Area max X |
| y2 | int | yes | Area max Y |
| z | int | yes | Z level |
| marked | bool | yes | true to mark, false to clear |
{"x1": 110, "y1": 130, "x2": 120, "y2": 140, "z": 2, "marked": true, "tiles": 121}Mark an area for crop planting. Validates tiles: skips occupied, water, and wrong terrain.
CLI: timberbot.py plant_crop x1:110 y1:130 x2:115 y2:135 z:2 crop:Carrot
| Field | Type | Required | Description |
|---|---|---|---|
| x1 | int | yes | Area min X |
| y1 | int | yes | Area min Y |
| x2 | int | yes | Area max X |
| y2 | int | yes | Area max Y |
| z | int | yes | Z level |
| crop | string | yes | Crop name (see Crop Names) |
{"x1": 110, "y1": 130, "x2": 115, "y2": 135, "z": 2, "crop": "Carrot", "planted": 28, "skipped": 8}Clear planting marks from an area.
CLI: timberbot.py clear_planting x1:110 y1:130 x2:115 y2:135 z:2
| Field | Type | Required | Description |
|---|---|---|---|
| x1 | int | yes | Area min X |
| y1 | int | yes | Area min Y |
| x2 | int | yes | Area max X |
| y2 | int | yes | Area max Y |
| z | int | yes | Z level |
{"x1": 110, "y1": 130, "x2": 115, "y2": 135, "z": 2, "cleared": true, "tiles": 36}Find valid planting spots in an area or within a building's work range.
CLI: timberbot.py find_planting crop:Kohlrabi id:-514366 or timberbot.py find_planting crop:Kohlrabi x1:68 y1:128 x2:72 y2:132 z:2
| Field | Type | Required | Description |
|---|---|---|---|
| crop | string | yes | Crop name (Kohlrabi, Pine, Birch, etc.) |
| id | int | no | Farmhouse/forester ID. returns spots within building range |
| x1, y1, x2, y2, z | int | no | Area scan (used when id is 0) |
{
"crop": "Kohlrabi",
"spots": [
{"x": 120, "y": 135, "z": 2, "moist": true, "planted": false},
{"x": 121, "y": 135, "z": 2, "moist": true, "planted": true}
]
}Get the work range tiles for a building. Same green circle the player sees when selecting a farmhouse, lumberjack, forester, gatherer, scavenger, or district center.
CLI: timberbot.py building_range id:-514366
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | yes | Building ID |
{
"id": -514366,
"name": "FarmHouse",
"tiles": 45,
"moist": 32,
"bounds": {"x1": 118, "y1": 130, "x2": 128, "y2": 145}
}Route a path from point A to point B using A* pathfinding over a 3D surface graph. Routes around buildings, natural resources, ruins, water, and terrain obstacles. Handles diagonal routes, multi-z transitions with auto-stairs/platforms, and reuses existing paths/stairs/platforms.
CLI: timberbot.py place_path x1:120 y1:130 x2:150 y2:160
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| x1 | int | yes | Start X | |
| y1 | int | yes | Start Y | |
| x2 | int | yes | End X | |
| y2 | int | yes | End Y | |
| style | string | no | "direct" | "direct" (shortest path) or "straight" (prefers straight lines) |
| sections | int | no | 0 | Stop after N stair/ramp crossings (0 = unlimited) |
| timings | bool | no | false | Include timing breakdown in response |
{"placed": {"paths": 12, "stairs": 1}, "skipped": 0, "connectorEdgesInGrid": 4}{"placed": {"paths": 8, "stairs": 1}, "skipped": 0, "connectorEdgesInGrid": 4, "stoppedAt": "130,142", "errors": [{"error": "stair failed at (130,142,3)"}]}{"placed": {"paths": 0}, "skipped": 0, "connectorEdgesInGrid": 0, "errors": [{"error": "A* found no route from (0,0) to (255,255). 0 connectors in graph"}]}| Field | Type | Description |
|---|---|---|
| placed.paths | int | Number of path tiles placed |
| placed.stairs | int | Number of stairs placed (omitted if 0) |
| placed.platforms | int | Number of platforms placed (omitted if 0) |
| skipped | int | Tiles that failed to place |
| connectorEdgesInGrid | int | Total stair/ramp edges found in the graph |
| stoppedAt | string | "x,y" where routing stopped (sections limit or error) |
| errors | array | Error objects with error or prefab+error fields |
| timings | object | When timings:true: totalMs, snapshotMs, graphMs, astarMs, placementMs, placementsAttempted, graphNodes, pathNodes, pathEdges |
Push notifications for game events. See webhooks.md for setup, configuration, the full event list (68 events), and circuit breaker behavior.
Register a webhook URL.
CLI: timberbot.py register_webhook url:http://localhost:9000/events events:drought.start,beaver.died
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | yes | URL to receive POST notifications |
| events | array | no | Event names to subscribe to. Omit for all events |
{"id": "wh_1", "url": "http://localhost:9000/events", "events": ["drought.start", "drought.end"]}List all registered webhooks.
CLI: timberbot.py list_webhooks
[{"Id": "wh_1", "Url": "http://localhost:9000/events", "events": ["drought.start", "drought.end"], "Disabled": false, "failures": 0}]Webhooks deliver batched JSON arrays (one POST per flush, default every 200ms):
[
{"event": "drought.start", "day": 45, "timestamp": 1711300000, "data": {"duration": 8}},
{"event": "beaver.died", "day": 45, "timestamp": 1711300000, "data": null}
]Remove a webhook by ID.
CLI: timberbot.py unregister_webhook id:wh_1
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | yes | Webhook ID from registration |
Reflection-based inspector for game internals. Navigates object graphs, lists fields/properties/methods, and calls methods with arguments. Results can be chained with $.
CLI: timberbot.py debug target:help
| Field | Type | Required | Description |
|---|---|---|---|
| target | string | yes | "help", "get", "fields", or "call" |
| path | string | varies | Dot-separated path from TimberbotService (e.g. "_scienceService.SciencePoints") |
| filter | string | no | (fields only) Filter members by name substring |
| method | string | varies | (call only) Method name to invoke |
| arg0..argN | string | no | (call only) Method arguments. Vector3Int as "x,y,z". "$" = last result |
| Target | Description | Required args |
|---|---|---|
help |
List available targets, roots, and examples | none |
get |
Navigate an object chain and dump the result | path |
fields |
List fields, properties, and methods on an object | path (optional), filter (optional) |
call |
Call a method on an object with typed arguments | path, method, arg0..argN |
{
"targets": ["help. this message", "get. navigate object chain", "fields. list members", "call. call method"],
"roots": ["_buildingService", "_entityRegistry", "_districtCenterRegistry", "_navMeshService", "..."],
"examples": [
"debug target:fields path:_navMeshService filter:Road",
"debug target:get path:_scienceService.SciencePoints",
"debug target:call path:_navMeshService method:AreConnectedRoadInstant arg0:120,142,2 arg1:130,142,2"
]
}- Dot-separated:
_fieldName.PropertyName.NestedField - List indexing:
_entityRegistry.Entities.[0] - GetComponent:
~TypeName(e.g.~MechanicalNode) - Chain from last result:
$.PropertyName
!!! warning "Debug only" This endpoint uses reflection on game internals. It may break on Timberborn updates. Not intended for production automation.
Profile internal hot paths and server micro-benchmarks. Requires debugEndpointEnabled: true in settings.json.
CLI: timberbot.py benchmark iterations:100
| Field | Type | Required | Description |
|---|---|---|---|
| iterations | int | no | Number of measured iterations per benchmark case (default 100) |
Returns per-case timing and GC allocation counts for internal micro-benchmarks. Benchmark work is queued and advanced incrementally across frames so the request may take longer wall-clock time without monopolizing one frame.
!!! warning "Debug only"
Requires debugEndpointEnabled: true. Returns {"error": "disabled: benchmark endpoint"} when disabled.
These are convenience methods in timberbot.py that have no direct HTTP equivalent.
Colored ASCII grid with terrain height display. Background shading encodes z-level, foreground characters represent entities.
timberbot.py map x1:112 y1:126 x2:132 y2:146| Char | Color | Meaning |
|---|---|---|
0-9 |
dim (green if moist) | Empty ground (digit = z % 10, background band = tens) |
~ |
blue | Water |
@ |
white | Entrance |
= |
yellow | Path |
D |
bright yellow | District center |
H |
yellow | Housing (Rowhouse, Barrack, Lodge) |
R |
yellow | Breeding pod |
F |
cyan | Farmhouse |
f |
green | Forester |
M |
white | Lumber mill / wood workshop |
S |
white | Science (Inventor, Numbercruncher) |
E |
bright yellow | Power (wheel, shaft) |
L |
red | Lumberjack |
G |
magenta | Gatherer |
K |
red | Hauling |
$ |
yellow | Warehouse / pile |
P |
bright blue | Pump |
W |
bright blue | Tank |
X |
cyan | Floodgate / dam / levee / sluice |
C |
red | Campfire |
T |
green | Grown tree (Pine, Birch, Oak, Maple, Chestnut) |
t |
dim green | Seedling |
B |
magenta | Berry bush, shrub |
/ |
yellow | Stairs |
_ |
yellow | Platform |
m |
white | Metalsmith, smelter |
g |
white | Gear workshop |
b |
magenta | Bot assembler, bot part factory |
z |
magenta | Charging station |
V |
bright blue | Fluid dump |
v |
bright blue | Shower, swimming pool |
~ |
green | Amenity (scratcher, bench, exercise plaza, medical bed) |
* |
red/yellow | Decoration (brazier, lantern, beaver bust) |
^ |
dim | Roof |
R |
dim | Ruins, relics |
Q |
bright yellow | Wonders (monuments, flame, tribute, repopulator) |
A |
bright blue | Aquifer drill |
i |
dim | Automation (lever, sensor, timer, memory, relay) |
x |
red | Explosives (dynamite, detonator) |
# |
dim | Terrain block, dirt excavator |
! |
yellow | Banners, firework launcher |
| |
dim | Fences (metal, wood) |
k |
bright green | Kohlrabi |
c |
bright green | Carrot |
p |
bright green | Potato |
w |
bright green | Wheat |
a |
bright green | Cassava |
s |
bright green | Sunflower |
n |
bright green | Corn |
e |
bright green | Eggplant |
y |
bright green | Soybean |
o |
bright green | Canola |
l |
bright green | Cattail |
d |
bright green | Spadderdock |
Background bands: z=0-9 (dark grays), z=10-19 (medium grays), z=20-22 (bright).
Find entities by name and/or proximity.
timberbot.py find source:buildings name:Pump x:120 y:130 radius:20A* path routing with auto-stairs/platforms. Wraps POST /api/path/place.
timberbot.py place_path x1:120 y1:130 x2:150 y2:160
timberbot.py place_path x1:0 y1:0 x2:255 y2:255 style:straight
timberbot.py place_path x1:120 y1:130 x2:150 y2:160 sections:1 timings:truePrepare a save launch using autoload.json.
- On Windows, this also opens Timberborn via Steam.
- On macOS, v1 writes
autoload.jsonand then expects you to open Timberborn manually.
timberbot.py launch settlement:Potato save:TomatoLive colony dashboard. Population, resources, weather, drought countdown, wellbeing breakdown, alerts.
timberbot.py topPersistent colony knowledge in Documents/Timberborn/Mods/Timberbot/memory/.
timberbot.py brain # live summary + persistent goal/tasks/locations
timberbot.py brain goal:"get to 77 wellbeing" # set persistent goal
timberbot.py set_location name:berries x:120 y:140 note:"south of DC" # save a named location
timberbot.py remove_location name:berries # remove a saved location
timberbot.py list_locations # list saved locations
timberbot.py add_task action:"build roads" # add task to work queue
timberbot.py update_task id:1 status:done # update task status
timberbot.py list_tasks # show all tasks
timberbot.py clear_tasks # remove done tasksbrain returns live summary (always fresh from /api/summary) plus persistent state from memory/brain.toon (goal, tasks, locations). Summary is never persisted. only goal, tasks, and locations survive between sessions. Set a persistent goal with brain goal:"text". The built-in in-game agent also uses brain internally during startup before it launches Claude/Codex.
- Building IDs are Unity
GameObject.GetInstanceID(). ephemeral, change every game session. Get current IDs fromGET /api/buildings. - Prefab names come from
GET /api/prefabs. Include faction suffix (e.g."LumberjackFlag.IronTeeth"). - Good names match Timberborn internal names:
Water,Log,Plank,Berries,Bread, etc. - Building names in responses are cleaned:
(Clone),.IronTeeth,.Folktailssuffixes removed.
| Value | Description |
|---|---|
VeryLow |
Lowest priority |
Normal |
Default |
VeryHigh |
Highest priority |
Two priority types exist per building: construction (while building) and workplace (when finished). Set both on new buildings.
| Value | Name | Description |
|---|---|---|
| 0 | south | Default facing |
| 1 | west | 90 degrees clockwise |
| 2 | north | 180 degrees |
| 3 | east | 270 degrees clockwise |
Coordinates always refer to the bottom-left corner of the footprint regardless of orientation. Python CLI accepts names (south, west, north, east or s/w/n/e).
Kohlrabi, Cassava, Carrot, Potato, Wheat, Sunflower, Corn, Eggplant, Soybean
| Value | Description |
|---|---|
None |
No importing |
Allowed |
Import when needed |
Forced |
Always import |
!!! bug "Ghost Buildings"
POST /api/building/place may create ghost buildings on invalid spots. The Place() callback fires and creates an entity even when placement is invalid. Python-side validation blocks most cases, but multi-tile overlaps or bad terrain can still ghost.
**Never test placement carelessly**. every failed `Place()` may create a ghost that needs manual cleanup.