Spoolis docs
Coding-agent quickstart
Paste one prompt into Claude Code or Codex to verify criteria and evidence in one sandbox call, then verify the signed receipt offline.
Run the one-call sandbox example
The prompt reads /machine.md, /skill.md, /openapi.json, /docs/quickstart.md, the Outcome Receipt schema, and the verifier contract. Its first product checkpoint is POST /api/sandbox/verify.
The request sends one deterministic completeness check, three evidence rows, and unitized economics. It asserts the endpoint's exact fourteen top-level response fields, 2 accepted records, 1 rejected record with a reason, 2 cents earned, the unilateral acceptance boundary, and the signed demo receipt before it verifies the receipt offline.
Run the Spoolis one-call unitized verification example end to end in the zero-login sandbox. Start with POST /api/sandbox/verify, not the bilateral lifecycle. Do not ask me for credentials or modify the current repository. Use the exact shell commands below. Stop and show the complete response if any assertion fails.
set -euo pipefail
BASE_URL=https://spoolis.com
for RESOURCE in machine.md skill.md openapi.json docs/quickstart.md schema/outcome-receipt-v1.schema.json docs/verify-receipt.md; do
RESOURCE_BODY=$(curl -fsS "$BASE_URL/$RESOURCE")
test -n "$RESOURCE_BODY"
done
echo "Read the machine index, skill, OpenAPI contract, quickstart, receipt schema, and verifier contract."
SESSION_JSON=$(curl -sS -X POST "$BASE_URL/api/sandbox/session")
TOKEN=$(SESSION_JSON="$SESSION_JSON" node -e 'const x=JSON.parse(process.env.SESSION_JSON); if (typeof x.token !== "string") throw new Error(JSON.stringify(x)); process.stdout.write(x.token)')
VERIFY_JSON=$(curl -sS -X POST "$BASE_URL/api/sandbox/verify" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"conditions":[{"description":"Every company record includes company_name and website_url.","deterministic_check":{"checker":"completeness","required_fields":["company_name","website_url"]}},{"description":"Every website_url is a valid URL.","deterministic_check":{"checker":"url_format","field":"website_url"}}],"max_amount_cents":3,"unit":{"total_units":3,"unit_amount_cents":1},"evidence":{"type":"dataset","rows":[{"company_name":"Acme Robotics","website_url":"https://acmerobotics.com"},{"company_name":"Blue Harbor Labs","website_url":"https://blueharbor.example"},{"company_name":"Cardinal Systems"}],"provenance":"api_response"}}')
VERIFY_JSON="$VERIFY_JSON" node -e 'const x=JSON.parse(process.env.VERIFY_JSON); const expected=["accepted","can_rerun","can_resubmit_evidence","decision_authority","earned_cents","next_step","receipt","receipt_url","rejected","rejections","settlement_eligible","spool_id","terminal","uncertain","unit_results","verification_run_id"]; if (JSON.stringify(Object.keys(x).sort()) !== JSON.stringify(expected)) throw new Error("Unexpected response fields: "+JSON.stringify(x)); if (typeof x.spool_id !== "string" || !x.spool_id.startsWith("spl_")) throw new Error("Missing spool_id"); if (x.earned_cents !== 2 || x.accepted !== 2 || x.rejected !== 1) throw new Error("Unexpected earned arithmetic: "+JSON.stringify(x)); if (!Array.isArray(x.rejections) || x.rejections.length !== 1 || x.rejections[0].unit !== 3 || typeof x.rejections[0].reason !== "string") throw new Error("Unexpected rejections: "+JSON.stringify(x.rejections)); if (!x.receipt || typeof x.receipt !== "object" || typeof x.receipt.signature !== "string") throw new Error("Expected a signed receipt"); if (x.receipt.environment !== "demo" || !x.receipt.units || x.receipt.units.total !== 3 || x.receipt.units.accepted !== 2 || x.receipt.units.rejected !== 1 || x.receipt.amounts.earned !== "0.02") throw new Error("Unexpected receipt: "+JSON.stringify(x.receipt)); const acceptance=x.receipt.agreement && x.receipt.agreement.acceptance; if (!acceptance || acceptance.mode !== "unilateral" || acceptance.provider_accepted_in_spoolis !== false || acceptance.accepted_by !== "initiator") throw new Error("Unexpected acceptance boundary: "+JSON.stringify(acceptance)); if (typeof x.receipt_url !== "string" || typeof x.verification_run_id !== "string") throw new Error("Missing receipt_url or verification_run_id"); if (typeof x.terminal!=="boolean"||typeof x.settlement_eligible!=="boolean"||typeof x.can_resubmit_evidence!=="boolean"||typeof x.can_rerun!=="boolean"||!x.next_step||x.next_step.type!==x.next_step.action||typeof x.decision_authority!=="string") throw new Error("Missing verification state contract: "+JSON.stringify(x)); require("node:fs").writeFileSync("outcome-receipt.json",JSON.stringify(x.receipt,null,2)+"\n"); console.log(JSON.stringify(x,null,2))'
npx -y @spoolis/cli verify outcome-receipt.json
RECEIPT_ID=$(VERIFY_JSON="$VERIFY_JSON" node -e 'process.stdout.write(JSON.parse(process.env.VERIFY_JSON).receipt.id)')
curl -sS "$BASE_URL/api/v1/receipts/$RECEIPT_ID/status"
Report the printed spool ID, 2 accepted records, the rejected record and its reason, 2 cents earned, verification run ID, demo receipt ID, receipt URL, offline receipt-verification result, and optional status response. State that the provider was identified but did not accept the criteria in Spoolis. Then explain that a payment stack may act on the verified earned amount under its own authorization policy. State clearly that this demo artifact does not authorize production settlement or move real funds.Run the $1 draft, amend, and confirm walk
The bilateral walk shows the full agent contract loop on a result-shaped job: 10 enrichment records at $0.10 per accepted record. Compiling creates a proposed draft with no economic commitment. The response's verification_summary shows how each requirement will actually be verified, downgrades flags any weakened deterministic plan, and the next block names the three moves: accept to confirm, amend to change conditions or unitization, abandon to discard.
Where the compiler maps an explicit requirement to buyer confirmation instead of a deterministic check, the walk amends the draft before accepting: it replaces the compiled conditions with a pinned row-count check, a required-fields completeness check, and per-record economics. Acceptance then confirms that exact contract. Verification accepts 9 of 10 records, rejects 1 for a missing required field, and earns $0.90 of the $1.00 cap on the signed demo receipt.
Run the Spoolis $1 draft, amend, and confirm walk end to end in the zero-login sandbox. Do not ask me for credentials or modify the current repository. Use the exact shell commands below. Stop and show the complete response if any assertion fails.
set -euo pipefail
BASE_URL=https://spoolis.com
curl -sS -X POST "$BASE_URL/api/sandbox/session" > session.json
TOKEN=$(node -p 'require("./session.json").token')
curl -sS -X POST "$BASE_URL/api/sandbox/compile" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"source":"Deliver 10 enriched company records for $1.00 total at $0.10 per accepted record. Every record must include company_name, website, and country."}' > compile.json
node -e 'const x=require("./compile.json"); if (x.status !== "compiled") throw new Error(JSON.stringify(x)); if (!x.next || x.next.status !== "draft") throw new Error("Expected a draft next block: "+JSON.stringify(x.next)); console.log("Draft "+x.spool.id); console.log(JSON.stringify(x.verification_summary, null, 2)); console.log("Downgrades: "+JSON.stringify(x.downgrades))'
# The summary shows how each requirement will actually be verified. Where the
# compiler mapped an explicit requirement to buyer confirmation instead of a
# deterministic check, amend the draft: replace the compiled conditions with
# pinned deterministic checks and per-record economics before confirming.
SPOOL_ID=$(node -p 'require("./compile.json").spool.id')
node -e 'const x=require("./compile.json"); const body={add_conditions:[{description:"Exactly 10 rows are delivered",required:true,verification_method:"deterministic",deterministic_check:{checker:"row_count",expected:10}},{description:"Every record includes the required fields: company_name, website, country",required:true,verification_method:"deterministic",deterministic_check:{checker:"completeness",required_fields:["company_name","website","country"]}}],remove_condition_ids:x.spool.conditions.map(c=>c.id),unitization:{total_units:10,unit_amount_cents:10}}; require("node:fs").writeFileSync("amend.json",JSON.stringify(body))'
curl -sS -X POST "$BASE_URL/api/sandbox/spools/$SPOOL_ID/amend" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d @amend.json > amended.json
node -e 'const x=require("./amended.json"); if (x.status !== "completed") throw new Error(JSON.stringify(x)); const s=x.verification_summary; if (!Array.isArray(s) || s.length !== 2 || !s.every(i=>i.method==="deterministic")) throw new Error("Expected two deterministic conditions: "+JSON.stringify(s)); const u=x.spool.economics.unitization; if (u.total_units !== 10 || u.unit_amount_cents !== 10) throw new Error("Unexpected unitization: "+JSON.stringify(u)); console.log("Amended. Every condition is now deterministic and the Spool pays per accepted record.")'
curl -sS -X POST "$BASE_URL/api/sandbox/spools/$SPOOL_ID/accept" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}' > accepted.json
node -e 'const x=require("./amended.json"); const rows=Array.from({length:9},(_,i)=>({company_name:"Example Co "+(i+1),website:"https://example"+(i+1)+".example.com",country:"US"})); rows.push({company_name:"Example Co 10",country:"US"}); const fs=require("node:fs"); for (const [file,checker] of [["ev-rowcount.json","row_count"],["ev-completeness.json","completeness"]]) { const cid=x.spool.conditions.find(c=>c.deterministic_check && c.deterministic_check.checker===checker).id; fs.writeFileSync(file,JSON.stringify({condition_id:cid,type:"dataset",source:"10 enrichment records, one missing website",metadata:{rows}})) }'
curl -sS -X POST "$BASE_URL/api/sandbox/spools/$SPOOL_ID/evidence" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d @ev-rowcount.json > ev-rowcount-result.json
curl -sS -X POST "$BASE_URL/api/sandbox/spools/$SPOOL_ID/evidence" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d @ev-completeness.json > ev-completeness-result.json
curl -sS -X POST "$BASE_URL/api/sandbox/spools/$SPOOL_ID/verify" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}' > verified.json
node -e 'const x=require("./verified.json"); const r=x.receipt; if (!r || typeof r.signature !== "string") throw new Error("Expected a signed receipt: "+JSON.stringify(x).slice(0,400)); if (r.environment !== "demo") throw new Error("Expected a demo receipt"); if (r.units.total !== 10 || r.units.accepted !== 9 || r.units.rejected !== 1) throw new Error("Unexpected units: "+JSON.stringify(r.units)); if (r.amounts.earned !== "0.90" || r.amounts.committed !== "1.00") throw new Error("Unexpected amounts: "+JSON.stringify(r.amounts)); const reason=r.units.rejection_summary[0]; if (reason.reason !== "nulls.absent" || reason.count !== 1) throw new Error("Unexpected rejection summary: "+JSON.stringify(r.units.rejection_summary)); require("node:fs").writeFileSync("outcome-receipt.json",JSON.stringify(r,null,2)+"\n"); console.log("9 of 10 records accepted, 1 rejected for a missing required field, $0.90 earned of the $1.00 cap.")'
npx -y @spoolis/cli verify outcome-receipt.json
Report the Spool ID, the before and after verification summaries, the amendment you sent, 9 accepted records, the 1 rejected record and its reason, $0.90 earned, the demo receipt ID, and the offline receipt-verification result. Explain that the compiled Spool was a proposed draft with no economic commitment, that amend replaced weak buyer-confirmation mappings with pinned deterministic checks before acceptance, and that acceptance confirmed that exact contract. State clearly that this demo artifact does not authorize production settlement or move real funds.What the receipt proves
A valid signature proves the receipt is genuine and unaltered, which agreement it is bound to, which checks ran with what evidence, what passed or failed, and what was earned. It does not prove the submitted evidence reflects real-world truth, does not authorize payment or settlement, and does not show a later correction or revocation without the optional online status check. The Outcome Receipt specification carries the full trust boundary.
Demo boundary
The receipt uses environment: "demo". It records 2 of 3 records accepted, 1 rejected with a reason, and 2 cents earned. The provider is identified but did not accept the criteria in Spoolis. The receipt does not authorize production settlement or move real funds.