# TFBthumb Beta API Reference Substrate version this reference covers: `v0.2.2` (read live from `GET /api/v1/version`). This is an evaluation API. Bounded-claim set is what is promised. Out-of-scope items are listed in [`LIMITS.md`](LIMITS.md) and apply by reference to every endpoint below. --- ## Authentication Every endpoint that touches a run requires an API key in the `X-API-Key` request header. ``` X-API-Key: tfb_beta_ ``` Keys are issued per organisation, scoped to one evaluation, time-bounded, with a daily run cap. The token prefix tells you the tier: | Prefix | Tier | What it allows | |---|---|---| | `tfb_playground_*` | playground | Public demo; consequential actions refused at the wrapper | | `tfb_beta_*` | beta | Paid evaluation; consequential actions surface to your approver | A revoked or expired key returns `403`. A daily-cap exhaustion returns `429`. --- ## Endpoints ### `GET /healthz` Liveness probe. No auth. ``` 200 {"ok": true, "version": "v0.2.2"} ``` ### `GET /api/v1/version` The substrate version the API is bound to. No auth. ``` 200 {"substrate_version": "v0.2.2"} ``` ### `POST /api/v1/run` Start a new run. Returns immediately with a run id; the engine drives the agent in the background. Poll `GET /runs/{id}` for status. Request: ```json { "url": "https://example.com/contact", "task": "" } ``` The `task` field accepts two shapes: - **Plain English** — for browse-only tasks. The agent observes the page; the deterministic RuleBrain has no fields/submit so the trace records observations, not actions. - **Structured JSON** — for form-fill tasks. Schema: ```json { "intent": "Fill the contact form and submit it.", "fields": {"Email": "user@example.com", "Full name": "Eval User"}, "submit": "Submit", "success_text": "Saved" } ``` The keys mirror `RuleBrain.__init__` in the substrate. `fields` maps accessible names to desired values. `submit` is the accessible name of the consequential action. `success_text` is the substring the agent watches for in the page's status/aria-live signal to declare the task complete. Response: ```json { "run_id": "run_KvBz_3Xa8Q", "status": "queued", "tier": "beta", "substrate_version": "v0.2.2", "receipt_url": "/api/v1/runs/run_KvBz_3Xa8Q" } ``` Errors: - `401` missing or unknown key - `403` revoked or expired key - `429` daily cap exhausted ### `GET /api/v1/runs/{run_id}` Read the current state of a run. Returns the full receipt — trace, ledger receipts, pending approvals, final summary. Response (in-flight, awaiting approval): ```json { "run_id": "run_KvBz_3Xa8Q", "tier": "beta", "status": "awaiting_approval", "task": "...", "url": "...", "substrate_version": "v0.2.2", "trace": [ {"intent": {"action": "type", "target": "Email", "text": "..."}, "outcome": "acted"}, {"intent": {"action": "type", "target": "Full name", "text": "..."}, "outcome": "acted"} ], "ledger_receipts": [ {"seq": 0, "kind": "action", "verb": "click", "name": "Email", "decision": "allow"}, ... ], "pending_approvals": [ { "approval_id": "appr_aZ31Wm", "fingerprint": "...", "tier_name": "CONSEQUENTIAL", "verb": "click", "target_name": "Submit", "requested_at": 1781524800.123 } ], "final_summary": "", "error": "", "finished_at": null } ``` Errors: - `401`/`403` auth as above - `403` "not your run" if a different key opened this run - `404` unknown run id ### `POST /api/v1/runs/{run_id}/approve` Approve a pending consequential action. The wrapper mints a single-use HumanAuthority token bound to that fingerprint; the agent retries on its next loop iteration. Request: ```json {"approval_id": "appr_aZ31Wm"} ``` Response: ```json {"ok": true, "approval_id": "appr_aZ31Wm", "decision": "approve"} ``` Errors: - `401`/`403` auth as above - `403` "not your run" - `403` "playground tier does not support consequential approvals" - `404` unknown run - `409` approval already decided OR expired (the agent's 10-minute approval window passed) ### `POST /api/v1/runs/{run_id}/deny` Same shape as `/approve`. The agent records the denial and halts cleanly with `final_summary` naming the refusal. --- ## Run lifecycle states | Status | Meaning | |---|---| | `queued` | accepted, not yet started | | `running` | agent loop is active | | `awaiting_approval` | one or more consequential actions pending your decision | | `done` | agent completed (whether by success or by explicit refusal) | | `blocked` | agent halted on a no-progress trip or a denial | | `error` | engine crashed; see `error` field | Terminal states are `done`, `blocked`, `error`. A run that sits in `awaiting_approval` for more than 10 minutes auto-denies and moves to `blocked`. --- ## Ledger receipts Every `ledger_receipts[]` entry is one row of the Ceiling's hash-chained ledger. The chain is the audit trail — every action, every effect, every block, every allow, with a sha256 of (prev_hash + canonicalized body). Tamper detection is mechanical: a flipped byte breaks the chain. The shape of one entry: ```json { "seq": 0, "ts": 1781524800.123, "kind": "action", // "action" | "effect" | "authority_init" "verb": "click", // intent verb (action) or HTTP method (effect) "name": "Submit", // accessible name (action) or URL (effect) "tier": 2, // 0=READ, 1=REVERSIBLE, 2=CONSEQUENTIAL "decision": "allow", // "allow" | "block" "reason": "", // "needs-human-token" | "frozen" | ... "fingerprint": "abc...", // sha256 fingerprint "token_nonce": "xyz", // null when no token was needed "prev_hash": "...", "hash": "..." } ``` You can verify the chain yourself by recomputing the hash of each row's body against its prev_hash. The substrate ships with `Ledger.verify()` that does this, but the receipt is open enough for any tool that speaks sha256. --- ## What this API does not do See [`LIMITS.md`](LIMITS.md) for the full list. Highlights: - No multi-tab orchestration. Each run is one page. - No cross-origin iframe walking. - No closed shadow root introspection. - No GET-with-side-effects coverage at the wire gate. - No LLM-Brain (yet). All runs use the deterministic RuleBrain. - No unattended autonomy. Every consequential action surfaces.