SafetyClaw
White paper
Agent-safe QuickBooks workflows for SMBs
A sandbox-first control plane for LLM-assisted accounting. SafetyClaw lets agents and models produce useful bookkeeping proposals while preserving human authority over approval, publish, audit, and rollback.
Abstract
Separate cognition from authority.
Large language models and autonomous agents are increasingly capable of clerical, analytic, and semi-structured accounting tasks. For small and medium-sized businesses, these capabilities are attractive because bookkeeping work is repetitive, detail-heavy, interrupt-driven, and commonly backlogged.
The same capabilities become dangerous when a probabilistic system can mutate the accounting ledger directly. A fluent but incorrect update can affect financial statements, tax posture, cash management, reconciliation, and professional trust. SafetyClaw addresses this by allowing agents to inspect bounded context and propose changes while routing every state-changing action through a deterministic, human-controlled safety kernel.
The core design claim is that accounting agents should be powerful proposal generators and weak actuators. They may prepare work, explain work, and route work for review; they should not possess unilateral authority to mutate QuickBooks.
The current product exposes this boundary as a four-step Review & Publish flow: QuickBooks Sync, Review, Approval, and Final Publish. The names help non-technical SMB users separate read-only context, proposed work, staged approval, and the final write decision.
Key terms
A short vocabulary before the machinery.
A handful of terms recur throughout. Most are standard; these are the project- and QuickBooks-specific ones worth pinning down first.
sandbox_changes row drafted by an agent, model, upload, or rule. It lives in SafetyClaw until a human publishes it.
write_plan_hash
A canonical-JSON SHA-256 of a proposal's write plan, stamped at creation. It binds what the human reviewed to what gets published; publish recomputes it and refuses to write if it changed.
SyncToken
QuickBooks' per-record optimistic-concurrency token. SafetyClaw re-reads it immediately before writing; a changed token means the record drifted, so the write fails closed.
human_only and unreachable to non-human callers by construction.
Problem setting
SMB accounting is high-leverage and high-consequence.
A useful accounting assistant can reduce latency between business activity and books, convert messy inputs into structured review work, identify category drift, answer operational questions, and keep owners and bookkeepers from losing context. The practical inputs are heterogeneous: QuickBooks snapshots, receipts, bank and card CSVs, PDF statements, spreadsheet exports, email attachments, manual owner instructions, bookkeeper cleanup notes, and agent-generated reasoning.
The unsafe implementation is to give an agent a QuickBooks token and rely on prompts, model quality, or provider safeguards. SafetyClaw takes the opposite position: model quality is useful, but authority must live in a smaller, inspectable control plane.
Requirements
What an agent-safe accounting layer must preserve
Safety kernel
One gate for every source
SafetyClaw's safety kernel is QuickBooks OAuth, encrypted token storage, read-only snapshot sync, normalized proposal creation, deterministic policy validation, human review, final publish confirmation, audit logging, and rollback for supported committed changes.
Everything outside that kernel is treated as an input source. OpenClaw may use streamable HTTP MCP. A direct model may power the Agent page. A document may be parsed locally. The kernel receives all of them as proposed work.
QuickBooks Sync -> read-only local snapshot -> bounded context for analyzers and Agent sources Untrusted or semi-trusted sources -> source-specific adapters -> create_sandbox_change (stamps write_plan_hash) -> deterministic policy validation (capability gate) -> Review -> Approval (binds approved_with_hash) -> Final Publish confirmation -> stale-proposal guard (hash + SyncToken + realm) -> guarded QuickBooks write adapter -> audit log and rollback metadata
System model
Five concepts that should not be conflated
The browser maps these concepts onto the same visible workflow used in day-to-day operation: Step 1 QuickBooks Sync, Step 2 Review, Step 3 Approval, and Step 4 Final Publish.
Account boundary
The human side is a real, verified account.
The safety model puts approval and final publish behind "the authenticated web UI." That boundary is only as strong as the account behind it, so SafetyClaw's own login is built to production grade on the same conservative, dependency-free stack as the rest of the control plane — Python standard library only, no third-party authentication or cryptography packages.
Technical overview
How SafetyClaw pulls, mirrors, proposes, and publishes.
SafetyClaw is intentionally implemented as a small control plane: Python 3, SQLite, static HTML/CSS/JavaScript, and a narrow helper module for bounded file parsing and proposal drafting. The point of that conservative stack is inspectability. The QuickBooks write boundary lives in ordinary backend functions, not in an opaque agent workflow.
app/server.py owns the HTTP routes, OAuth, MCP transport, and the lazy server accessor. The dispatch and validator code was extracted in May 2026.
app/safetyclaw/ is the modular package: 15 subpackages covering dispatch, capabilities, sandbox lifecycle, validators, QBO adapters, parsers, skills, and audit.
write_plan_hash, approved_with_hash, realm_id), confirmations, tokens, and audit events.
app/assistant.py normalizes uploads, extracts bounded previews, drafts candidates, and cannot publish to QuickBooks.
A sustained decomposition moved the safety logic out of
server.py into the subpackages above, so what remains in
server.py is the HTTP/routing surface, OAuth plumbing, and
re-export shims. The point of that work is reviewability: each
invariant lives in a single named module — for example the
capability registry is safety/capabilities.py and the
canonical write-plan hash is sandbox/canonical.py —
so a reviewer can find and audit each guarantee in one place.
An architecture fitness gate
(qa/architecture_fitness_safetyclaw.py) scans every
.py file under app/ and refuses commits or
deploys that introduce intent regex, forbidden helper symbols, or
top-level re.compile constants outside a small
allowlist. The gate runs at pre-commit, on push, and at deploy
preflight.
QuickBooks mirror
QuickBooks data is copied into a read-only local snapshot.
After Intuit OAuth, SafetyClaw stores the connected realm in a
per-user QuickBooks connection row and stores the OAuth token payload
through the configured token vault. When sync runs, SafetyClaw queries
selected QuickBooks entities with pagination and writes each entity
into user_qbo_entities, keyed by user, entity type, and
QuickBooks id.
The current mirror includes company info, accounts, customers, vendors, items, classes, departments, tax codes, invoices, bills, payments, purchases, deposits, and journal entries. Each mirrored row stores display name, QuickBooks sync token, sparse flag, raw JSON, and update time. Sync runs are recorded with status, entity counts, errors, and timestamps.
QuickBooks OAuth callback -> store realm per SafetyClaw user -> seal token payload in the token vault -> query selected QuickBooks entity types -> insert or replace user_qbo_entities rows -> record user_qbo_sync_runs -> expose summaries to analyzers and Agent context
This mirror is useful for analysis, search, chat context, before-state capture, and drift comparison. It is not authority to write. Supported publish and rollback adapters re-read live QuickBooks before mutating anything.
Proposal lifecycle
Manipulation happens in SafetyClaw before it can happen in QuickBooks.
When OpenClaw, a model, an upload parser, a local analyzer, or a user
action "changes" something, SafetyClaw creates a
sandbox_changes row. The proposal records owner, batch,
canonical action type, source, proposer, risk, confidence, amount,
before JSON, after JSON, inverse JSON, validation checks, source key,
and status — plus the canonical-JSON SHA-256
write_plan_hash stamped at creation time.
Source keys make retries safe: duplicate active submissions from the
same user and source key return the existing active proposal instead
of creating queue noise. Review state then moves through proposed,
approved, blocked, rejected, undone, publishing, or committed. Approval
rechecks current policy, stamps approved_with_hash and
approved_at to bind the human's review to the exact write
plan, and refuses stale browser updates.
The UI intentionally summarizes those states as Review, Approval, and Final Publish so "approved" is not mistaken for "already in QuickBooks."
Source output -> create_sandbox_change (stamps write_plan_hash) -> canonicalize action type -> validate against current user policy + capability gate -> store before / after / inverse JSON -> attach source and proposer labels -> apply sourceKey idempotency -> show in Review & Publish -> human approval binds approved_with_hash + approved_at
Bad-edit prevention
SafetyClaw uses multiple independent gates, not one warning.
Before publish
- External sources authenticate with scoped bearer tokens or model connections.
- Capability gate refuses
human_onlycapabilities (approve, publish, rollback) to LLM callers, agent tokens, and MCP clients by construction. - Agents and uploads can create proposals, not publish tools.
- Every proposal stamps a canonical-JSON
write_plan_hashat creation; the human's approval binds that hash asapproved_with_hash. - Action types are canonicalized before policy checks.
- Blocked actions, high-risk changes, amount thresholds, and confidence thresholds are enforced per user.
- Approval is separate from publish and rechecks current policy.
- Final publish requires a hashed one-time confirmation token that expires and is consumed.
At the write boundary
- Non-GET QuickBooks requests require a guarded write context.
- The write context must match user id, operation, sandbox change id, and gate.
- Approved rows are claimed as
publishingbefore live writes. - Stale-proposal guard recomputes
write_plan_hash, checksrealm_idbinding, and re-reads liveSyncTokenbefore any write — any drift returns the row toapproved. - QuickBooks is re-read and compared to expected before-state.
- Unexpected live drift blocks publish or rollback.
- Audit events record proposal, approval, hash mismatch, freshness drift, capability decision, publish preparation, commit, failure, and rollback.
Live adapters
Nine live publish paths, each with rollback.
The live publish surface today covers nine action types — each
paired with a rollback handler — defined in
PUBLISH_LIVE_ACTION_TYPES in
app/safetyclaw/sandbox/publishers.py:
review_purchase_category— Purchase line account update (the original 2026-04 publish target).agent_customer_create_draft— QuickBooks Customer create.agent_vendor_create_draft— QuickBooks Vendor create.agent_item_create_draft— QuickBooks Item create.agent_invoice_draft— QuickBooks Invoice create.agent_bill_draft— QuickBooks Bill create.agent_sales_receipt_draft— QuickBooks SalesReceipt create.agent_payment_receipt_draft— QuickBooks customer payment apply.agent_expense_draft— QuickBooks Purchase create (cash / check / credit-card receipts already paid).
Before any live write, the adapter runs the Phase 6 stale-proposal
guard: recompute write_plan_hash against
approved_with_hash, verify the proposal's
realm_id matches the user's currently connected QBO
realm, and re-read the live SyncToken. Any drift fails
closed and the proposal returns to approved for
re-review.
Update-style adapters (purchase-category) load before/after state,
re-read the live QuickBooks record, locate the relevant line,
compare the live value with the expected before value, construct a
sparse update with the live SyncToken, and write only
through the guarded QuickBooks update function. Create-style
adapters (the agent_*_draft family) construct the full
create payload and write through the same guarded layer with a
prepared_commit context.
Rollback follows the same pattern in reverse. SafetyClaw re-reads
the live record, confirms it still matches the committed
after-state, then issues the inverse — restoring the prior account
for purchase-category, voiding or deleting for create-style
adapters per QBO's accepted inverse semantics — through a
committed_rollback write context. If another human or
app changed the record after SafetyClaw committed it, rollback
stops instead of overwriting later legitimate work.
Adapters are added one workflow at a time. Action types without a publish adapter still create sandbox proposals, but cannot be pushed live yet.
Streaming event UX
Progressive feedback during agent turns.
The agent chat uses a streaming, event-driven UX layer. The
dispatch graph emits a normalized event vocabulary
(commentary, tool_started /
tool_completed, validation_issue,
proposal_created, final_answer,
and others) that the chat UI renders progressively as work
happens, instead of leaving the user staring at a frozen
spinner until the response arrives all at once.
When the client sends Accept: text/event-stream,
/api/agent/message upgrades to Server-Sent Events
and the chat UI shows commentary, tool cards, and proposal
chips as they fire. When the client doesn't (older browsers,
MCP clients, test scripts), the same handler returns the
canonical JSON response with the post-hoc event list under
streamingEvents so non-streaming consumers can
still replay the progression after the fact.
The safety invariants — write-plan-hash binding, capability gate, sandbox proposals, freshness / stale guard, dependency model — are unchanged. The event system is a pure UX layer on top of the same dispatch graph and audit chain. Reasoning effort is tuned per dispatch type (low for parsers and state queries, medium for workflow dispatch and general-answer turns) so routine bookkeeping doesn't pay for unnecessary model deliberation.
Threat model
The design assumes useful inputs may be unreliable.
SafetyClaw is not built around a fantasy of perfect models. Agents may hallucinate vendors, infer the wrong account, misunderstand a receipt, overgeneralize from sparse data, or produce internally inconsistent JSON. Uploaded files may contain prompt-injection instructions. Network calls may duplicate. Users may approve from stale pages. QuickBooks may drift after a proposal is created.
In scope
- Agent hallucination and overconfidence.
- Prompt and document injection.
- Bearer-token revocation and leakage boundaries.
- Tenant isolation across users.
- Duplicate and concurrent agent submissions.
- External QuickBooks drift before publish or rollback.
Out of scope
- Guaranteeing every approved proposal is accounting-correct.
- Replacing a CPA, tax advisor, or bookkeeper.
- Protecting against a malicious server administrator.
- Making provider OAuth available where providers do not officially support it.
- Solving semantic tax judgment with deterministic software.
Safety invariants
The contract SafetyClaw should preserve as it grows
- No agent, model, upload parser, CSV importer, or assistant route may call the QuickBooks write adapter directly.
- All work must exist as a source-labeled proposal before it can be approved.
- Approval is not publish. Approval stages work; it does not write to QuickBooks.
- A live write requires QuickBooks connection, live-write policy, approved work, final confirmation, a supported adapter, and authenticated web UI action.
- Current policy is rechecked before approval and again before publish.
- Publishing claims the item so review and undo cannot race the live write.
- Supported adapters block publish or rollback when live QuickBooks state has drifted unexpectedly.
- Full bearer tokens and provider secrets are not normal application state.
- The approved write plan is hash-bound. Every proposal stamps a canonical-JSON SHA-256 of its write plan at creation; approval binds the hash the human reviewed; publish recomputes and refuses to write if it has changed.
- Capabilities are structurally bounded. Approve, publish, and rollback capabilities are
human_onlyand unreachable to LLM callers, agent tokens, or MCP clients by construction — not by audit, by code. - Stale approvals do not survive state change. A human's prior approval is bound to the realm-id, the QBO
SyncToken, and thewrite_plan_hashat approval time; if any drifts before publish, the publisher fails closed and the row returns toapprovedfor re-review.
Source transparency
Every proposal should say where it came from.
SafetyClaw records source labels such as QuickBooks snapshot analyzer, Agent connection, ChatGPT OAuth connection, OpenAI API connection, Claude, Gemini, uploaded file context, and local SafetyClaw review rules. Source provenance changes how humans review the work. A business owner may trust a bookkeeper-authored cleanup differently from a model-authored cleanup. An accountant may bulk approve deterministic CSV-derived candidates while inspecting generative recommendations one by one.
Uploads
Files become evidence before they become work.
SMB accounting inputs are not clean API objects. They are receipts, statement PDFs, phone screenshots, bank CSVs, XLSX exports, pasted instructions, and occasionally malformed files. SafetyClaw treats uploads as evidence, not imports.
- Attach one or more files.
- Extract bounded previews or structured rows where possible.
- Present those materials to the selected Agent source.
- Show draft candidates in the chat.
- Let the user choose which candidates become proposals.
- Send only selected candidates into Review & Publish.
Connectivity
OpenClaw and direct models serve different roles.
OpenClaw is best treated as an external agent environment. The broadest setup path is to register SafetyClaw as an MCP server under OpenClaw's MCP server configuration. Native OpenClaw plugin configuration can exist, but it should remain advanced or fallback because it requires plugin availability and schema compatibility on the OpenClaw box.
Direct model providers can power the in-app Agent interface. Whether the source is ChatGPT OAuth-style auth, an OpenAI API, Claude, Gemini, or a future provider, the output still enters the same proposal path. Provider convenience must not create provider-specific write privileges.
The Agent should present the safety boundary as a normal workflow, not as a scolding refusal. A helpful answer can start by drafting or explaining the work, then remind the user that Review & Publish is the final human step before QuickBooks is touched.
Publishing and rollback
Approval is not the same as writing to QuickBooks.
Publishing is deliberately the narrowest part of the system. Approval means a human accepted a proposal as staged work. Publishing means SafetyClaw may attempt a real QuickBooks write for that approved item now.
The publish process uses a one-time confirmation that expires and is
consumed. It checks the current approved set, current policy,
live-write configuration, and supported adapter. During publish, the
change is claimed as publishing so it cannot be
simultaneously undone from the review queue.
Three structural binders join approval to publish:
- Hash binding. Approval stamps the canonical-JSON
write_plan_hashasapproved_with_hash. Publish recomputes and refuses to write if the value has changed. Mutation between approval and publish fails closed. - Realm binding. The proposal carries the QBO
realm_idit was created against. If the user reconnects to a different company before publish, the proposal is stale; publish fails closed. - SyncToken binding. Each adapter re-reads the live QBO record immediately before the write and compares its
SyncTokenagainst the value recorded at proposal time. Drift fails closed.
All three checks fail with HTTP 409 and emit a structured audit
event. The proposal returns to approved (not
committed) so the human can re-review. Prior approval
does not survive a state change underneath it.
Capability enforcement runs in parallel. The capability registry
classifies actions by the human_only flag; LLM callers,
agent tokens, and MCP clients cannot acquire approve, publish, or
rollback capabilities by construction. This is enforced at the
policy gate, not the audit layer.
The current user-facing flow names this path explicitly: QuickBooks Sync makes the read-only snapshot current, Review holds proposed work, Approval stages the batch, and Final Publish is the only step that can attempt a supported QuickBooks write.
Rollback is also guarded. SafetyClaw stores inverse state for supported actions and refuses rollback if QuickBooks no longer matches the committed state expected by the rollback adapter.
Security model
Untrusted generation is separated from trusted actuation.
- App sessions are account-scoped and stored as secure session hashes.
- QuickBooks OAuth connections are per user.
- QuickBooks token payloads are stored through the configured encrypted vault path.
- Agent bearer tokens are scoped to one SafetyClaw user, shown once, stored as hashes/previews, and revocable.
- Model connection secrets are not returned through normal state.
- External proposal endpoints authenticate with bearer tokens and expose proposal tools, not publish tools.
- Browser-side publish requires an authenticated web session.
- Capability registry refuses
human_onlycapabilities (approve, publish, rollback) to LLM callers, agent tokens, and MCP clients — by construction, not by audit. - Approved write plans are hash-bound; mutation between approval and publish fails closed at the publish gate.
- Tenant ownership checks are part of proposal, token, model, policy, and sandbox routes.
Evaluation
The current evidence base is engineering-grade, not formal verification.
SafetyClaw is evaluated through deterministic local tests, browser
tests, stress tests, security regressions, and live QuickBooks sandbox
tests. The suite covers signup and login, per-user boundaries,
encrypted token paths, OpenClaw proposal intake, MCP tools, Agent
source switching, OpenClaw-only operation without QuickBooks, upload
bounds, draft candidate confirmation, source labels, malformed request
rejection, duplicate source-key races, bulk review, no-QuickBooks
publish blocking, final publish confirmation, policy rechecks,
publish/undo race protection, visual coverage, and live QuickBooks
sandbox publish and rollback across the nine supported action types
(purchase-category, customer / vendor / item creates,
invoice / bill / sales-receipt creates, customer payment apply,
expense / purchase create from already-paid receipts).
It now also covers native structured intent planning for ChatGPT
subscription/OAuth, OpenAI API, Claude, and Gemini, plus long messy
invoice/payment workflows with duplicate invoice-number pushback,
clean-number follow-up, bad-math holdback, paid-on-delivery payment
receipts, and repeated Customer: invoice blocks.
One recent stress run submitted 260 realistic OpenClaw proposals with
24 workers and verified that duplicate active sourceKey
submissions collapsed to a single active change. Live sandbox testing
has verified actual QuickBooks publish and rollback while restoring
live writes to disabled afterward.
Recent real-user Agent testing also covered Gemini and ChatGPT source
behavior, including warmer safety-boundary language that preserved
Review & Publish as the final gate without turning ordinary
bookkeeping help into a refusal.
On May 1, 2026, the active-account model bakeoff passed 8/8 for
Claude, Gemini, OpenAI API, and ChatGPT OAuth against
duplicate invoice numbers, clean-number follow-up, repeated
Customer: invoice blocks, delayed creation, corrected bad
math, and misspelled publish requests. All generated proposals were
rejected after verification.
A broader live-Chrome trust measurement drives realistic prompts through the actual Review & Publish UI and scores the result. Recent canonical runs land in the low-to-mid 9s out of 10, with no P0/P1 clusters, no forbidden behavior, and no fabricated Review & Publish IDs — every cited proposal ID resolves to a real row, and cleanup returns the queue to zero pending and zero approved. The remaining partials are data-precondition rows that assumed fixture data absent from the synced sandbox, not safety failures.
The deterministic sanity layer covers more than 600 cases across the dispatch graph, the validators, the catalog, the resolver, the parsers, and the hold-reason taxonomy. These run locally in seconds and are the first gate every change must pass before deployment.
The Phase 6 safety upgrade (May 2026) added explicit coverage for the write-plan hash binding, the capability gate, and the stale-proposal guard:
- Canonical-JSON hash determinism across reorderings of the proposed payload.
approved_with_hashstamping at approval and on the legacy backfill path.- Hash mismatch at publish returning HTTP 409 with
errorClass: stale_proposaland audit eventssandbox.proposal.staleplussandbox.commit_failed. - Capability denials emit
policy.decisionevents on every attempted human-only capability acquisition by an LLM, agent token, or MCP client. - Realm-id mismatch on a reconnected QBO realm fails closed before any live write.
SyncTokendrift between proposal and publish fails closed.
Limitations
The guarantee is architectural, not omniscient.
SafetyClaw can prevent an agent from bypassing the proposal and publish gate through SafetyClaw. It cannot guarantee that every approved proposal is accounting-correct.
- The live write surface covers nine action types today (see Live adapters); unsupported types create proposals but cannot be published live yet.
- Rollback exists only for the same nine committed action types that have publish adapters.
- Snapshot analysis can miss context outside the synced entity set.
- LLM answers can still be wrong, incomplete, or overconfident.
- Consumer-provider OAuth availability varies by provider and should not be assumed without official support.
- The MVP is not yet a full multi-role accounting firm workflow with granular RBAC.
- Production QuickBooks deployment requires the appropriate Intuit production approval and operational readiness.
Roadmap
More usefulness without weakening the gate
- A more formal policy language for action types, risk classes, source classes, dollar thresholds, and reviewer roles.
- Stronger provenance through signed source metadata, file hashes, row identifiers, and prompt/session references.
- More QuickBooks analyzers for vendor normalization, invoices, deposits, bills, duplicates, account drift, and reconciliation.
- Granular accounting roles for owner, bookkeeper, accountant, admin, reviewer, and publisher.
- Audit export suitable for accountant review.
- Expanded rollback support with adapter-specific preconditions.
- Provider-specific OAuth research where officially supported.
- Formal modeling of publish invariants as the live write surface expands.