Kajota × KeeperHub is an escrow release rail for onchain merchants. Coach — a Gemini-backed agent — watches deposits, applies deterministic rules to decide releases, narrates the reasoning in plain English, and audits its own KH workflow against the trap catalogue we shipped in KH's canonical docs. KeeperHub's Turnkey wallet signs the release under EIP-7702 delegation. All in ~15 seconds from the moment Coach greenlights the release.
Connect a Sepolia wallet and put down 0.10 test USDC.
You'll sign one approve and one
deposit — and then Coach will
refuse to release, printing the rule that failed.
Confirm receipt and the same rules pass; only then does KeeperHub sign. You never pay
release gas.
This depositId is already released — a second attempt reverts on-chain with the escrow's idempotency guard. Real KH keeper signing path, no USDC required.
…
demo
A server-side loop polls Sepolia for every deposit handed to it, reads escrow state
with a raw eth_call, and runs the identical
rules engine. When the verdict is release it invokes
the KeeperHub workflow directly — no click, no wallet, no page loaded.
It ships dry-run by default: chain reads and
rule evaluation are real, only the signature is withheld until an operator sets
KH_WATCHER_LIVE=1.
Same rules the deployed POST /coach/audit-workflow
endpoint runs — ported to the browser so it's instant, no server round-trip.
Load a canned demo (bad / clean) or paste your own workflow JSON to see the report.
Each button POSTs /concierge/coach/should-release
with one signal changed and renders whatever comes back. Deterministic rules produce
release hold reject,
and a narration explains why. Same signals in → same verdict out, every time.
// Content-Type: application/json { "workflow": { // the KH workflow definition, verbatim "name": "Release escrow on Sepolia", "nodes": [ { "id": "trigger-1", "type": "trigger", "data": { "label": "HTTP", "config": { "triggerType": "HTTP" } } }, { "id": "step-1", "type": "action", "data": { "config": { "actionType": "web3/write-contract", "network": "11155111", "web3Connection": "default", "contractAddress":"0x5998…1776", "abiFunction": "release", "functionArgs": "[\"{{@trigger-1:HTTP.depositId}}\"]", "abi": "[{…}]" } } } ], "edges": [ { "source": "trigger-1", "target": "step-1" } ] }, "workflowRef": "optional-label" // echoed back in the report }
// 200 OK — a workflow that trips every trap { "passed": false, "counts": { "error": 5, "warn": 1, "info": 1 }, "issues": [ { "trap": "silently-ignored-integration-id", "severity": "error", // error | warn | info "path": "nodes[1].data.config.integrationId", "detail": "`integrationId` is a reserved config key that the validator accepts but the write-contract action ignores. The signing wallet is routed by `web3Connection` instead.", "fix": "replace with `\"web3Connection\": \"default\"`" } // … one object per detected trap ], "summary": "Audit failed: 5 errors (+ 1 warning) across 1 write-contract action. Fix errors before executing…", "actionNodesScanned": 1, "workflowRef": "optional-label" }
# paste this straight into a terminal — returns a full report card curl -sS -X POST https://kajota-hub.onrender.com/concierge/coach/audit-workflow \ -H 'content-type: application/json' \ -d '{"workflow":{"name":"probe","nodes":[{"id":"s","type":"action", "data":{"config":{"actionType":"web3/write-contract", "function":"release","integrationId":"int_x", "functionArgs":["{{@trigger.body.depositId}}"]}}}], "edges":[]}}' # GET the same URL in a browser for a self-describing info payload curl -sS https://kajota-hub.onrender.com/concierge/coach/audit-workflow
| Field | Type | Notes |
|---|---|---|
| workflow | object required | A KeeperHub workflow definition — name, nodes[], edges[]. Paste it verbatim from GET /api/workflows/{id} or from the editor's JSON view. |
| workflowRef | string | Optional label echoed back on the report. Handy when auditing many workflows in a batch. |
| passed | bool ↩ response | true when zero error-severity issues. Warnings and info never block. |
| issues[].severity | enum | error — mis-routes or fails at execute time · warn — accepted but non-canonical · info — advisory. |
| issues[].fix | string | A copy-pasteable correction, not just a description of the problem. |
| actionNodesScanned | int | How many web3/write-contract nodes were examined. 0 means nothing in this workflow is in scope. |
integrationId to route the signing wallet and
the API validates it, saves it, and then ignores it entirely — your write goes out on
whatever wallet the org policy resolves. The real routing key is
web3Connection (accepts
"default", "eoa",
or "safe:<id>").
Invalid function arguments JSON
at execution — half an hour after your last save.
{{@trigger.body.x}} — silently
resolves to nothing. Stored HTTP triggers use a slot-scoped path:
{{@trigger-1:HTTP.x}}. And the POST body must be wrapped under
input.
function and method; functionName works as an undocumented legacy alias."default" (org policy), "eoa", or "safe:<id>". The wallet is your org's Turnkey wallet, resolved automatically. integrationId looks like the right field but is silently ignored.{{@trigger.body.x}}. And your POST body must wrap fields under input.
Signature accepts one Solidity function like release(bytes32),
transfer(address,uint256), or castVote(uint256,uint8).
Everything on the right is generated live — copy the whole block into your
KH workflow's web3/write-contract action.
Notice: web3Connection (trap #01, the silently-ignored field) ·
functionArgs as a JSON-encoded string (trap #02) ·
trigger template {{@trigger-1:HTTP.…}} (trap #03) ·
canonical abiFunction (with functionName also accepted as a legacy alias) ·
minimal abi with only the fragment KH parses at deploy time.
release(bytes32 depositId) · …