Pet activity monitor

A photo-based activity tally with explicit gaps, plus an offline control card.

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

Preview offline card · Complete HTML interaction body · Machine-readable recipe

What runs where

Controller integration

Only explicit decision-model/weather requests leave the phone; 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 state/tally first. To change the definition, export required history, DELETE old watch, POST revised definition. 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": {
      "label": "Pet activity samples",
      "data": [
        "camera"
      ],
      "camera": "rear",
      "questions": {
        "activity": {
          "type": "choice",
          "instructions": "Classify only the visible animal in this photo. If absent or ambiguous choose unclear. Do not infer identity or health.",
          "criteria": {
            "resting": "An animal is lying still.",
            "eating": "An animal visibly eats at a bowl.",
            "drinking": "An animal visibly drinks at a bowl.",
            "other": "Visible activity fits none of those.",
            "unclear": "Absent, occluded or uncertain."
          }
        }
      },
      "model": "clef",
      "interval_s": 30,
      "window": 1,
      "min_readings": 1,
      "expires_in_s": 7200,
      "repeat": true,
      "local": {
        "change_check": true,
        "sensitivity": "high",
        "learn_noise": true,
        "max_reuse_s": 300
      },
      "max_paid_per_hour": 20,
      "tally": {
        "track": "activity",
        "labels": [
          "resting",
          "eating",
          "drinking",
          "other"
        ],
        "min_confidence": 0.7,
        "gap_s": 90,
        "keep_days": 7
      }
    }
  },
  "response": {
    "status": 201,
    "body": {
      "watch_id": "w0123456789ab",
      "label": "Pet activity samples",
      "user_paused": false,
      "created_at": "2026-11-04T10:00:00Z",
      "expires_at": "2026-11-04T12:00:00Z",
      "interval_s": 30,
      "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": {
        "label": "Pet activity samples",
        "data": [
          "camera"
        ],
        "camera": "rear",
        "questions": {
          "activity": {
            "type": "choice",
            "instructions": "Classify only the visible animal in this photo. If absent or ambiguous choose unclear. Do not infer identity or health.",
            "criteria": {
              "resting": "An animal is lying still.",
              "eating": "An animal visibly eats at a bowl.",
              "drinking": "An animal visibly drinks at a bowl.",
              "other": "Visible activity fits none of those.",
              "unclear": "Absent, occluded or uncertain."
            }
          }
        },
        "model": "clef",
        "interval_s": 30,
        "window": 1,
        "min_readings": 1,
        "expires_in_s": 7200,
        "repeat": true,
        "local": {
          "change_check": true,
          "sensitivity": "high",
          "learn_noise": true,
          "max_reuse_s": 300
        },
        "max_paid_per_hour": 20,
        "tally": {
          "track": "activity",
          "labels": [
            "resting",
            "eating",
            "drinking",
            "other"
          ],
          "min_confidence": 0.7,
          "gap_s": 90,
          "keep_days": 7
        }
      },
      "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)"
    }
  },
  "evidence": "Synthetic contract fixture, not a device observation."
}

stop

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

Extensions

Source evidence