HTTP API

The pet serves this text at GET /. It is generated from the engine (engine/door/api.cpp) by tools/specdoc.py; edit the engine, not this file.

NestBox
========

A pixel pet on a small screen. Anything on your network can make it react with one HTTP POST.

POST /v1/event   (JSON body, Content-Type: application/json, up to 16 KB)

  {"source": "tests", "event": "error", "level": "error", "for": 30}

  source   who is sending: "my-agent", "tests", "ci", "home"... (default "webhook")
  event    what happened. Known events:
             working                  -> watches its laptop work until done, error or stop (or `for` s);
                                         if you type too, it works at its desk itself
             busy                     -> busy at its desk until done, error or stop: a build, a deploy, tests
             done                     -> a happy pose with a heart
             celebrate                -> celebrates, bigger than done: shipped, merged, a birthday
             needs_you                -> calls you with a "!" and `text` as its question for a minute,
                                         then waits with a small "!" and a status line showing how long,
                                         calling again every 10 min (3 times). The line turns grey after
                                         30 min without word from that session and clears an hour after
                                         its last word (or after `for` s). Asking again refreshes it; a new
                                         question calls again. It ends when that session sends working,
                                         done or stop. Sessions wait separately ("needs you x2").
                                         A tap stops the calling; a double press dismisses all waiting.
             your_turn                -> "Myagent: your turn" in the status bar until you are back, tap
                                         the pet, or that session sends its next event
             error                    -> a sad reaction with a red cross; if the user is away, it stays
                                         slumped with "Myagent failed - N min" in red until they are back
             down                     -> stays down until the same sender sends up, then celebrates:
                                         a red build, an outage
             alert                    -> a startled hop: a doorbell, a message, a timer
             warning                  -> oops, lighter than error
             music ("playing": true|false) -> notes and swaying on top of anything; dances when free
             quiet                    -> sits still and reactions wait (60 min, or `for`): a meeting, focus
             break                    -> eats (20 min, or `for`)
             party                    -> a party (30 min, or `for`)
             stop                     -> clears what this source/session set (working, busy, down,
                                         needs you, its state and text); nobody else's
             shake                    -> dizzy (sent by the device's motion sensor)
             pet                      -> petted. A tap on the device itself also acknowledges `until` "seen".
           any other event reacts by its level; `state` and `anim` reach every other state and animation
  level    info (a small nod) | happy | alert | error (default info)
  session  optional: keeps several sessions of one source apart
  text     optional: a few words in a speech bubble over the pet (up to 72 characters)
  color    optional: the text's colour, a name (orange, red, green, blue, yellow, purple, pink, cyan,
           white, gray) or "#rrggbb". Remembered per source; the default is cream.
  size     optional: small | medium | large, for a speech bubble and the ring (default medium)
  status   optional: a line in the status bar along the top (e.g. "standup in 5 min"), one per source
           (8 at most), in `color`, until replaced, `"status": ""`, stop, or `for` seconds (default 1 h).
           The bar shows one line at a time in large letters; several take turns, long ones scroll.
           Coding agent sessions (see GET /agents) show their current step and running helpers here.
  meter    optional: a progress ring in the top-right corner, one per source: a number, filled clockwise in
           `color` (e.g. a playlist's progress, a download, a usage limit); null removes it. Each message sets
           the whole ring. Up to 3 show, the most recently updated on the right. A ring stays 24 h after its
           last update, or `for` seconds; stop clears it. GET /v1/state lists them.
  max      optional, with meter: the value of a full ring, above 0 (default 100); the number is kept in 0..max
  label    optional, with meter: a few characters inside the ring (default: the percentage of max, e.g. 62)
  at       optional: pet (a speech bubble, default) | top | bottom: a caption, a row in the status bar's style
           (top: just under the status bar)
  state    optional: ask for any named state. Moods are held `for` seconds (default 60): idle, wander,
           working, typing_fast, watching, needs_you, asleep, busy, party, dancing, fighting, eating,
           quiet, down. Reactions play once: done, error, dizzy, waking_up, celebrate, noted, surprised,
           petted, grumpy, confused, yawn, oops.
  anim     optional: play any animation the pet has, by name (e.g. "Hop", "Dance"), `for` seconds
           (default 3) or "once": true (one run). A pet without it plays its Idle.
  face     optional, with anim: you | front-right | right | back-right | away | back-left | left |
           front-left (or "row": 0-7). Default: you; with `to`, the way it arrives.
  to       optional: walk there first, then play: [x, y] in screen pixels (as GET /v1/state reports; the nearest
           spot it may stand on) or "home" (its place by the desk). Its time starts when it has arrived.
  steps    optional: a chain, up to 16, each {to, face, anim, for | once, text}, played in order; then it
           goes back to its own life. A spot it can't reach within 30 s is skipped. An agent calling for
           you or a reaction comes first, then the chain carries on. `stop` from the same source ends it.
           GET /v1/pets gives each animation's directions (8, or 1: it always faces you) and ms (one run).
  for      optional: seconds a working/needs-you lasts, or a reaction plays
  until    optional: "seen" keeps a state, text, status or needs_you until the user taps the pet (or presses
           its button), the same source/session sends its next message, or an hour (or `for`) passes.
           needs_you does this by default; e.g. {"source":"deploy","text":"deploy failed","state":"down",
           "until":"seen"}.
  pet      optional: bring out another pet: its name, "next" or "previous" (a short cross-fade)

  Reply: {"ok": true, "reaction": "done", "state": "done"}

  The Mac companion app sends {"source": "mac", "event": "activity", "kps": 2.5, "idle_s": 0,
  "key_ms": 300, "cpu": 30, "locked": false} about once a second: key presses per second (a count, never
  which keys), ms since the last key, seconds since the last input, CPU load, plus "time" (unix seconds) and
  "utc_offset" (seconds east of UTC) so the scene can follow the time of day. A switched-off signal is left
  out (kps is sent as -1). When these messages stop for 20 s the pet goes to sleep.

  The device's side button sends {"event": "button", "press": "single" | "double" | "long"}: a single
  press pats the pet; a double press dismisses everything waiting for you (with nothing waiting, it brings
  out the next pet); a long press toggles hush (quiet: no reactions). Only the device itself can send it.

  The pet answers only to its own name and addresses, and refuses writes from another site's web page.

GET /v1/state  what the pet is doing now (JSON)
GET /v1/pets   every pet in detail (its words, scene, what each state plays, animations) and which is out (JSON)
GET /v1/spec   every event word, every state and the animations it plays, in order (the pet format's contract)
GET /agents    the integration guide for coding agents: give your agent this address to set itself up
GET /llms.txt  the same guide

A reply may carry a "warning": an unknown event without a level, an animation this pet doesn't have, or an
unknown state still works (a nod, its Idle), and the warning says what to send instead.

Debugging: {"event": "hold", "state": "asleep"} keeps it in one state (wander, idle, working,
typing_fast, needs_you, asleep); {"event": "auto"} lets it decide again.

Examples:
  curl -d '{"source":"tests","event":"error"}' http://nestbox.local/v1/event
  curl -d '{"source":"deploy","event":"done"}' http://nestbox.local/v1/event
  curl -d '{"text":"lunch!","color":"green","anim":"Hop"}' http://nestbox.local/v1/event
  curl -d '{"state":"asleep","for":600,"text":"do not disturb"}' http://nestbox.local/v1/event
  curl -d '{"source":"me","steps":[{"to":[60,200],"face":"left","anim":"Eat","for":4,"text":"snack"},
            {"anim":"Hop","once":true},{"to":"home"}]}' http://nestbox.local/v1/event