Spoolis docs
Accept work between two parties
A Spool is a versioned acceptance policy for a piece of work. Use the bilateral lifecycle when both parties need to accept that policy inside Spoolis before work is verified.
When both parties agree in Spoolis
Use the bilateral lifecycle when both parties need to accept the agreement inside Spoolis. This path keeps agreement, acceptance, evidence submission, verification, and the receipt as separate inspectable steps.
Environment. Prefer /api/v1 for both environments. A sandbox session token selects isolated demo behavior with simulated settlement; a production API key selects production behavior. The /api/sandbox/* tree remains a working compatibility alias. This guide starts in the sandbox on purpose.
Who is doing the work? A person or business provider is paid through payout setup: the provider must finish payout onboarding before the buyer can fund. An agent or software provider is paid through the agent settlement path with wallet authority. In the API this choice is the Spool type field, and it defaults to service, the person or business path. Send type: "agent_task" for an agent or software provider. The creation response includes a lane block that states which path was selected.
Sessions are short-lived and have compile, Spool, verify, input-size, and record limits. Dataset evidence is limited to 100 records. Always inspect the stable code and status envelope, including on HTTP 200. If a session expires or reaches a limit, mint a fresh one and repeat the flow.
The MCP server also exposes sandbox scenarios, including the data enrichment flight scenario, through run_sandbox_scenario.
1. Mint a sandbox session
Run one request and keep the returned token.
curl -sS -X POST https://spoolis.com/api/sandbox/session > session.jsonExpected checkpoint: session.json contains token, expires_at, and limits.
Keep: token.
2. Compile the agreement
Compile exact economics and two Live deterministic checks. See the single method-status table. Schema acceptance does not mean an executor is live.
TOKEN=$(node -p 'require("./session.json").token') && curl -sS https://spoolis.com/api/sandbox/compile -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"source":"A buyer pays for 100 enrichment records at $1.00 per accepted record, total $100.00. Exactly 100 records are required. Every record must include id and status.","economics":{"unitization":{"total_units":100,"unit_amount_cents":100}},"conditions":[{"description":"Exactly 100 records","required":true,"verification_method":"deterministic","deterministic_check":{"checker":"row_count","expected":100}},{"description":"Each record must include id and status","required":true,"verification_method":"deterministic","deterministic_check":{"checker":"completeness","required_fields":["id","status"]}}]}' > compile.jsonExpected checkpoint: status is compiled and spool.environment is demo.
Keep: the Spool ID and both condition IDs.
3. Accept the sandbox Spool
Model acceptance in the sandbox.
TOKEN=$(node -p 'require("./session.json").token') && SPOOL_ID=$(node -p 'require("./compile.json").spool.id') && curl -sS -X POST "https://spoolis.com/api/sandbox/spools/$SPOOL_ID/accept" -H "Authorization: Bearer $TOKEN" > accept.jsonExpected checkpoint: status is completed.
Keep: the same Spool ID.
4. Submit row-count evidence
Send 100 complete rows to the row-count condition.
TOKEN=$(node -p 'require("./session.json").token') && SPOOL_ID=$(node -p 'require("./compile.json").spool.id') && BODY=$(node -e 'const x=require("./compile.json");const condition_id=x.spool.conditions.find(c=>c.deterministic_check?.checker==="row_count").id;const rows=Array.from({length:100},(_,i)=>({id:"record-"+(i+1),status:"accepted"}));process.stdout.write(JSON.stringify({condition_id,type:"dataset",source:"100 enrichment records",metadata:{rows}}))') && curl -sS -X POST "https://spoolis.com/api/sandbox/spools/$SPOOL_ID/evidence" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "$BODY" > count-evidence.jsonExpected checkpoint: status is completed.
Keep: count-evidence.json for troubleshooting.
5. Submit completeness evidence
Send 98 complete rows and 2 rows missing status.
TOKEN=$(node -p 'require("./session.json").token') && SPOOL_ID=$(node -p 'require("./compile.json").spool.id') && BODY=$(node -e 'const x=require("./compile.json");const condition_id=x.spool.conditions.find(c=>c.deterministic_check?.checker==="completeness").id;const rows=Array.from({length:100},(_,i)=>i<98?{id:"record-"+(i+1),status:"accepted"}:{id:"record-"+(i+1)});process.stdout.write(JSON.stringify({condition_id,type:"dataset",source:"98 accepted and 2 rejected enrichment records",metadata:{rows}}))') && curl -sS -X POST "https://spoolis.com/api/sandbox/spools/$SPOOL_ID/evidence" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "$BODY" > fields-evidence.jsonExpected checkpoint: status is completed.
Keep: fields-evidence.json for troubleshooting.
6. Produce the Outcome
Run the acceptance step and save the signed receipt.
TOKEN=$(node -p 'require("./session.json").token') && SPOOL_ID=$(node -p 'require("./compile.json").spool.id') && curl -sS -X POST "https://spoolis.com/api/sandbox/spools/$SPOOL_ID/verify" -H "Authorization: Bearer $TOKEN" > lifecycle-verify.json && node -e 'const x=require("./lifecycle-verify.json");if(x.code||x.status==="limit_reached"||x.status==="resting")throw new Error(JSON.stringify(x));if(x.verdict!=="PARTIAL"||x.receipt.units.accepted!==98||x.receipt.units.rejected!==2||x.receipt.amounts.earned!=="98.00")throw new Error(JSON.stringify(x));require("node:fs").writeFileSync("bilateral-outcome-receipt.json",JSON.stringify(x.receipt,null,2)+"
")'Expected checkpoint: verdict PARTIAL, 98 accepted, 2 rejected, and earned 98.00.
Keep: bilateral-outcome-receipt.json and its receipt ID.
Optionally check receipt status
This public, keyless endpoint adds correction, revocation, or advisory state. Receipt IDs are content-derived 96-bit values, so the lookup does not expose sequential identifiers. A well-formed ID with no known receipt returns state: "unknown" with HTTP 200.
RECEIPT_ID=$(node -p 'require("./outcome-receipt.json").id') && curl -sS "https://spoolis.com/api/v1/receipts/$RECEIPT_ID/status"Expected checkpoint: a newly issued receipt reports state: "current". A replaced receipt reports state: "superseded" with its reason and, when available, the superseding receipt ID.
Spoolis maintains the acceptance state around the chosen judgment, determines what counted and what was earned, and signs the Outcome Receipt. A wallet, marketplace, payment system, or other workflow decides what happens next under its own policy.
Hand a production Spool to the counterparty
In production, a bilateral Spool reaches the other party through its share link, not through any hidden channel. The creation response includes share_url and a handoff block. Send share_url to the counterparty through any channel you already use: the link identifies the Spool, and the counterparty signs in on the web or accepts with their own API key to claim their side.
If you set a valid email address as the counterparty's contact, Spoolis also emails them the link. The handoff.invite_email field reports whether that email was sent, so never assume the other party was notified unless it says sent.
Move to production
Replace POST /api/sandbox/verify with POST /api/v1/verify and send a full-scope or verify-scoped production API key. Each account includes 10 verification runs and 10 free-text compiles per calendar month. Included usage is consumed first; paid compile and verification work then uses the prepaid usage balance. Transaction fees are separate. External settlement remains the default. Use managed settlement only with a full-scope key and a compatible configured payment path.
The bilateral path then forks on who does the work. Person or business provider: the provider finishes payout setup from the Spool page before the buyer can fund. Agent or software provider (type: "agent_task"): create a production API key, request production enablement for your account, then follow the wallet authority guide to register your principal, grant bounded authority with your own credentials, and verify the onchain permission. Enablement is an account status change, not a separate integration.
Agent with a wallet
POST https://spoolis.com/api/v1/verify/x402 is the keyless production door. Send the request without payment first. The 402 response quotes the exact price for that request in USDC on Base. Pricing varies with the verification work required, up to the published maximum.
- Send the request below with no payment. The response is HTTP 402 with a PAYMENT-REQUIRED header quoting the exact price for this request in USDC on Base (x402 v2, exact scheme). Send the request without payment first. The 402 response quotes the exact price for that request in USDC on Base. Pricing varies with the verification work required, up to the published maximum.
- Sign the quoted amount with an x402 client and retry the same request with the PAYMENT-SIGNATURE header.
- The same request now returns per-unit accepted/rejected/uncertain results, earned value for accepted units only, and a signed Outcome Receipt with a receipt id.
{
"settlement": "external",
"max_amount_cents": 3,
"unit": {
"total_units": 3,
"unit_amount_cents": 1
},
"conditions": [
{
"description": "Every row includes status",
"deterministic_check": {
"checker": "completeness",
"required_fields": [
"status"
]
}
}
],
"evidence": {
"type": "dataset",
"provenance": "api_response",
"rows": [
{
"id": 1,
"status": "complete"
},
{
"id": 2,
"status": "complete"
},
{
"id": 3
}
]
}
}