Build a custom verifier
Compose the eight clause kinds. There is no plugin API.
A "custom verifier" is a combination of the eight built-in clause kinds — the registry is closed, and a clause with any other kind is rejected at the API boundary.
{
"clauses": [
{ "kind": "http.status", "equals": 200 },
{ "kind": "json.jsonpath", "path": "$.invoice.total", "equals": "84.00" },
{ "kind": "data.freshness", "maxAgeSeconds": 60 }
]
}No plugin API
You cannot ship code that runs inside the engine. There is no webhook the engine
calls to ask your opinion, and no way to register a ninth kind.
The closed registry is what makes a spec provable: spec_hash is meaningful
because everyone evaluating it runs the same code. A plugin would make the hash
a reference to whatever your server happened to return that day.
The eight kinds
kind | Fields | Passes when |
|---|---|---|
http.status | equals | The status equals it exactly. |
json.schema | schema | The body validates against that named schema. |
json.jsonpath | path, equals? | The path resolves; if equals is given, it matches. |
file.sha256 | digest | The bytes hash to that digest. |
data.freshness | maxAgeSeconds | The payload is no older than that. |
uptime.window | windowSeconds, minRatio | Coming soon. Reads a supplied uptimeRatio. |
chain.event | topic | Coming soon. Reads supplied emittedTopics. |
llm.judge | rubric, minScore | Coming soon. Reads a supplied judgeScore. |
Design the spec against the bench first
POST /bench/run evaluates clauses against a deliverable you describe. No key,
nothing held, nothing recorded.
curl -sS https://api.gowarrant.xyz/bench/run \
-H "Content-Type: application/json" \
-d '{
"clauses": [
{ "kind": "http.status", "equals": 200 },
{ "kind": "json.jsonpath", "path": "$.invoice.total", "equals": "84.00" }
],
"deliverable": {
"reached": true,
"status": 200,
"raw": "{\"invoice\":{\"total\":\"84.00\"}}"
}
}'The deliverable is described, not fetched: reached, status, raw (up to
128 kB), generated_at, uptime_ratio, emitted_topics, judge_score. now
overrides the clock so a freshness clause is reproducible.
Every clause is reported
The engine evaluates the whole list rather than stopping at the first failure. Three clauses where the second fails returns three details — you learn the status was fine, the schema matched, and the data was stale. That is the difference between a useful escalation and a shrug.
score is the share that passed. On the bench it is a fraction 0–1;
everywhere it is stored it is basis points 0–10000. Half passing is 0.5
here and 5000 on an escalation.
Aim for clauses that fail informatively
Two clauses that each check one thing beat one clause that checks two, because
the failure tells you which half broke. http.status plus data.freshness
separates "the endpoint is down" from "the endpoint is serving yesterday's
data", and those want different policy outcomes — the first is usually
alwaysEscalate, the second is often a partial release.
Saved verifiers
verifier_id on a warrant references a saved verifier instead of inline
clauses. Saved verifiers are created in the console; there is no
POST /v1/verifiers. Most integrations never need one — clauses travel inline
and are hashed into spec_hash either way.
Amounts
Clauses carry none, but the warrant does: an integer string of the smallest
unit, ^[0-9]+$.
"8400000" 8.40 USDC
8400000 rejected — a JSON number
"8.40" rejected — not the smallest unit