Situation counter

Spot and count one camera situation, like a pot boiling over or a dog licking itself.

Documentation revision 1 · Contract reference API 54 · Installed readiness unknown

Import to my phone One file, one approval on the phone: it sets up the watch, the card and keeps running with no agent. Applet file

Preview offline card · Test it in the card simulator · Complete HTML interaction body · Machine-readable recipe

What runs where

Controller integration

Only Clef photo checks leave the phone (model left out, so Clef is the default for a camera watch); direct or relay transport still requires a running reachable phone.

Boundaries

Lifecycle

start

POST /v1/watches with the start example; retain the returned watch_id. Then render/post the card with resource_id.

update

GET /v1/watches/{id}/tally first. To change the situation or timing, archive the tally, DELETE the old watch, POST the revised body. No watch PATCH exists.

pause

POST /v1/watches/{id}/pause with {"paused":true}; resume with false. Confirm user_paused from the returned metadata. Expiry is unchanged.

stop

DELETE /v1/watches/{id}; confirm deleted equals the saved id and watch.ended reason deleted. Archive needed tally BEFORE deletion. Cancel the interaction separately.

Failures and costs

Complete synthetic contract examples

These fixtures demonstrate contracts. They are not observations from a phone. Replace image/resource IDs with returned authorized IDs. Load request.json as the complete body of POST /v1/interactions; it contains the full offline HTML.

start

{
  "id": "start",
  "request": {
    "method": "POST",
    "path": "/v1/watches",
    "body": {
      "situation": "pot_boiling_over",
      "data": [
        "camera"
      ],
      "camera": "rear",
      "interval_s": 20,
      "expires_in_s": 7776000,
      "max_paid_per_hour": 30,
      "local": {
        "change_check": true,
        "sensitivity": "normal",
        "max_reuse_s": 300
      }
    }
  },
  "response": {
    "status": 201,
    "body": {
      "watch_id": "wsituation01",
      "label": "Pot boiling over",
      "user_paused": false,
      "created_at": "2026-11-04T10:00:00Z",
      "expires_at": "2027-02-02T10:00:00Z",
      "interval_s": 20,
      "data": [
        "camera"
      ],
      "checks": 0,
      "matches": 0,
      "last_match_at": null,
      "readings_held": 0,
      "last_check": null,
      "usage": {
        "model": "clef",
        "answered_locally": 0,
        "paid_calls": 0,
        "paid_cost_usd": 0,
        "paid_calls_last_hour": 0,
        "max_paid_per_hour": 30,
        "skipped_over_paid_cap": 0,
        "change_check": true
      },
      "spec": {
        "interval_s": 20,
        "question": null,
        "data": [
          "camera"
        ],
        "expires_in_s": 7776000,
        "questions": {
          "pot_boiling_over": {
            "instructions": "Is a pot or pan on the stove boiling over, with liquid or foam spilling over its rim? Judge from the newest photo: how likely is it that this is happening in it now?",
            "type": "noul"
          }
        },
        "paid_calls_per_hour_at_most": 30,
        "label": "Pot boiling over",
        "min_readings": 1,
        "max_paid_per_hour": 30,
        "local": {
          "max_reuse_s": 300,
          "learn_noise": true,
          "sensitivity": "normal",
          "change_check": true
        },
        "callback_url": null,
        "cooldown_s": 600,
        "repeat": true,
        "context": null,
        "model": "clef",
        "window": 1,
        "callback_when": [],
        "camera": "rear",
        "tally": {
          "min": 0.7,
          "gap_s": 180,
          "kind": "noul",
          "min_episode_readings": 1,
          "rules": [
            {
              "cooldown_s": 300,
              "id": "alert",
              "do": {
                "path": "/v1/interactions",
                "method": "POST",
                "body": {
                  "kind": "notice",
                  "text": "Seen on the camera just now.",
                  "title": "Pot boiling over",
                  "priority": "urgent"
                }
              },
              "when": {
                "of": "pot_boiling_over",
                "within_s": 120,
                "at_least": 1
              }
            }
          ],
          "track": "pot_boiling_over",
          "seen": null,
          "labels": null,
          "keep_days": 30
        },
        "situation": "pot_boiling_over"
      },
      "tally_today": null,
      "rules_fired": 0,
      "callback_secret": null,
      "first_check_in_s": 0,
      "delivery": "watch.matched and watch.ended go to your registered callback if you have one, and to GET /v1/events (both need the interactions permission); watch.rule (a rule fired) goes the same way"
    }
  },
  "evidence": "Synthetic contract fixture, not a device observation."
}

own-words

{
  "id": "own-words",
  "request": {
    "method": "POST",
    "path": "/v1/watches",
    "body": {
      "situation": {
        "describe": "the cat is sleeping on the armchair",
        "alert": false
      },
      "data": [
        "camera"
      ],
      "camera": "rear",
      "interval_s": 60,
      "expires_in_s": 2592000,
      "max_paid_per_hour": 20,
      "local": {
        "change_check": true,
        "sensitivity": "normal",
        "max_reuse_s": 900
      }
    }
  },
  "response": {
    "status": 201,
    "body": {
      "watch_id": "wsituation02",
      "label": "The cat is sleeping on the armchair",
      "user_paused": false,
      "created_at": "2026-11-04T10:00:00Z",
      "expires_at": "2026-12-04T10:00:00Z",
      "interval_s": 60,
      "data": [
        "camera"
      ],
      "checks": 0,
      "matches": 0,
      "last_match_at": null,
      "readings_held": 0,
      "last_check": null,
      "usage": {
        "model": "clef",
        "answered_locally": 0,
        "paid_calls": 0,
        "paid_cost_usd": 0,
        "paid_calls_last_hour": 0,
        "max_paid_per_hour": 20,
        "skipped_over_paid_cap": 0,
        "change_check": true
      },
      "spec": {
        "interval_s": 60,
        "question": null,
        "data": [
          "camera"
        ],
        "expires_in_s": 2592000,
        "questions": {
          "situation": {
            "instructions": "Does the photo show this: the cat is sleeping on the armchair? Judge from the newest photo: how likely is it that this is happening in it now?",
            "type": "noul"
          }
        },
        "paid_calls_per_hour_at_most": 20,
        "label": "The cat is sleeping on the armchair",
        "min_readings": 1,
        "max_paid_per_hour": 20,
        "local": {
          "max_reuse_s": 900,
          "learn_noise": true,
          "sensitivity": "normal",
          "change_check": true
        },
        "callback_url": null,
        "cooldown_s": 600,
        "repeat": true,
        "context": null,
        "model": "clef",
        "window": 1,
        "callback_when": [],
        "camera": "rear",
        "tally": {
          "min": 0.7,
          "gap_s": 180,
          "kind": "noul",
          "min_episode_readings": 1,
          "rules": [],
          "track": "situation",
          "seen": null,
          "labels": null,
          "keep_days": 30
        },
        "situation": "situation"
      },
      "tally_today": null,
      "rules_fired": 0,
      "callback_secret": null,
      "first_check_in_s": 0,
      "delivery": "watch.matched and watch.ended go to your registered callback if you have one, and to GET /v1/events (both need the interactions permission); watch.rule (a rule fired) goes the same way"
    }
  },
  "evidence": "Synthetic contract fixture, not a device observation: own words count under the track id \"situation\"."
}

stop

{
  "id": "stop",
  "request": {
    "method": "DELETE",
    "path": "/v1/watches/wsituation01"
  },
  "response": {
    "status": 200,
    "body": {
      "deleted": "wsituation01"
    }
  },
  "evidence": "Synthetic contract fixture, not a device observation."
}

Extensions

Source evidence