Spoolis docs
API reference
The v1 API is a bearer-authenticated Spool resource. Production keys bind created Spools to your account.
Audit an invoice
POST /api/v1/invoice-audit
Send exactly one of invoice_csv or invoice_pdf_base64, plus the record CSV and column mapping. The PDF field accepts a bare standard base64 string up to 3,000,000 decoded bytes. PDF reads are limited to 200 per API key per day. Through the API, the PDF is read in server memory for the request and is not stored. On the invoice audit page, the PDF never leaves the browser and only the extracted lines go to the check.
Replay a promise
POST /api/v1/promise-replay
Send candidate vendor promise terms, historical units and optional corrections, an explicit as_of timestamp, and optional sensitivity grids. Spoolis deterministically computes how those terms would have measured the supplied history and reports only hypothetical remedies. The submitted history is processed in memory and is never persisted or logged.
{
"promise": { "promise_version": "replay/1", "window": "month", "threshold_bp": 9500, "finality_days": 7, "uncertain_treatment": "count_as_miss", "fee_basis": "delivered_value", "remedy_tiers": [{ "min_rate_bp": 0, "remedy_bp": 1000 }], "min_units": 1 },
"units": [{ "unit_id": "unit-1", "customer_id": "customer-1", "delivered_at": "2026-01-01T00:00:00.000Z", "verdict": "pass", "value_cents": 1000 }],
"as_of": "2026-02-08T00:00:00.000Z"
}This is a replay of your own history under terms you chose.
It does not predict future results.
Whether to offer a promise, and on what terms, is your decision.OpenAPI document
Import the OpenAPI 3.1 document from https://spoolis.com/openapi.json. It describes the production and sandbox routes, operation IDs, request shapes, examples, and error schema.
Endpoint index
This compact index is rendered from openApiDocument.paths. Use OpenAPI for request and response schemas instead of treating this as a second schema.
| Method and path | Auth | Environment | Purpose | Guide |
|---|---|---|---|---|
POST /api/v1/verify | Bearer | Production | Run acceptance in one call | Guide |
POST /api/v1/verify/x402 | Bearer | Production | Run acceptance on delivered agent work and return per-unit results, earned value, and a signed Outcome Receipt. | Guide |
POST /api/v1/keys/exchange | Bearer | Production | Exchange an API key grant | Guide |
GET /api/v1/usage | Bearer | Production | Get account usage | Guide |
POST /api/v1/credits/packs/mpp | Bearer | Production | Buy a credit pack with MPP | Guide |
POST /api/v1/credits/packs/x402 | Bearer | Production | Buy a credit pack with x402 | Guide |
GET /api/v1/credits/balance | Bearer | Production | Get credit balance | Guide |
POST /api/v1/invoice-audit | Bearer | Production | Compare a CSV or PDF invoice with a record CSV | Guide |
POST /api/v1/promise-replay | Bearer | Production | Replay a vendor promise against historical outcomes | Guide |
POST /api/v1/policy-compiler | Bearer | Production | Compile an outcome policy | Guide |
POST /api/v1/policy-compiler/confirm | Bearer | Production | Confirm an outcome policy | Guide |
POST /api/v1/spools | Bearer | Production | Create a Spool | Guide |
GET /api/v1/spools/{id} | Bearer | Production | Get a Spool | Guide |
GET /api/v1/spools/{id}/discrepancies | Bearer | Production | List discrepancies | Guide |
POST /api/v1/spools/{id}/discrepancies | Bearer | Production | Open a discrepancy | Guide |
POST /api/v1/discrepancies/{id}/resolve | Bearer | Production | Resolve a discrepancy | Guide |
POST /api/v1/discrepancies/{id}/dismiss | Bearer | Production | Dismiss a discrepancy | Guide |
GET /api/v1/spools/{id}/settlement/grant | Bearer | Production | Get a per-Spool settlement grant | Guide |
POST /api/v1/spools/{id}/settlement/grant | Bearer | Production | Create a per-Spool settlement grant | Guide |
GET /api/v1/spools/{id}/settlement/grant/template | Bearer | Production | Get a per-Spool grant template | Guide |
POST /api/v1/spools/{id}/settlement/signatures | Bearer | Production | Submit direct settlement signatures | Guide |
GET /api/v1/spools/{id}/quote | Bearer | Production | Inspect an agent quote | Guide |
POST /api/v1/spools/{id}/quote | Bearer | Production | Request an agent quote | Guide |
POST /api/v1/spools/{id}/invite | Bearer | Production | Create a counterparty invitation | Guide |
GET /api/v1/spools/{id}/events | Bearer | Production | List Spool events | Guide |
POST /api/v1/spools/{id}/{action} | Bearer | Production | Act on a Spool | Guide |
GET /api/v1/receipts/{receipt_id} | Bearer | Production and demo | Get an Outcome Receipt | Guide |
POST /api/v1/receipts/{receipt_id}/consumptions | Bearer | Production and demo | Record Outcome Receipt consumption | Guide |
POST /api/v1/receipts/{receipt_id}/supersede | Bearer | Production and demo | Supersede an Outcome Receipt | Guide |
GET /api/outcome-index | Bearer | Production | Read the Outcome Index dataset | Guide |
GET /api/v1/receipts/{receipt_id}/status | Bearer | Production and demo | Get Outcome Receipt status | Guide |
POST /api/v1/receipts/{receipt_id}/reviews | Bearer | Production and demo | Request a human review | Guide |
GET /api/v1/reviews/{review_id} | Bearer | Production | Get a review request | Guide |
POST /api/v1/reviews/{review_id}/decision | Bearer | Production | Decide a review | Guide |
POST /api/v1/receipts/{receipt_id}/disputes | Bearer | Production and demo | Open a dispute | Guide |
GET /api/v1/disputes/{dispute_id} | Bearer | Production | Get a dispute | Guide |
POST /api/v1/attestations/verify | Bearer | Production and demo | Verify an attestation | Guide |
GET /api/sandbox/session | Bearer | Demo | Get sandbox session state | Guide |
POST /api/sandbox/session | Bearer | Demo | Mint a sandbox session | Guide |
POST /api/sandbox/compile | Bearer | Demo | Compile a sandbox Spool | Guide |
POST /api/sandbox/verify | Bearer | Demo | Verify a sandbox result in one call | Guide |
GET /api/sandbox/spools/{id} | Bearer | Demo | Get a sandbox Spool compatibility alias | Guide |
GET /api/sandbox/spools/{id}/events | Bearer | Demo | List sandbox Spool events compatibility alias | Guide |
POST /api/sandbox/spools/{id}/{action} | Bearer | Demo | Act on a sandbox Spool | Guide |
GET /api/v1/sources | Bearer | Production | List evidence sources | Guide |
POST /api/v1/sources | Bearer | Production | Add an evidence source | Guide |
GET /api/v1/sources/{id} | Bearer | Production | Get an evidence source | Guide |
POST /api/v1/sources/{id}/sync | Bearer | Production | Enqueue an evidence source sync | Guide |
POST /api/v1/sources/{id}/webhook | Bearer | Production | Receive signed source evidence | Guide |
GET /api/v1/spools/{id}/facts | Bearer | Production | List normalized facts | Guide |
GET /api/v1/spools/{id}/matches | Bearer | Production | List claim matches | Guide |
POST /api/v1/spools/{id}/claims | Bearer | Production | Add a vendor-reported claim | Guide |
POST /api/v1/spools/{id}/match | Bearer | Production | Run deterministic outcome matching | Guide |
Base URL and authentication
Authorization: Bearer spk_live_...
Content-Type: application/jsonCreate a production key on the API keys page. Creating a key requires sign-in, and the full key is shown once. Store it securely and send it as a bearer token. Missing, invalid, unknown, or revoked keys return 401. Static spk_test_ keys remain available in configured development and test environments.
Anyone can open and review a shared Spool. Accepting through the production API requires an authenticated key for a party to that Spool.
Verify a result in one call
POST /api/v1/verify
A full-scope or verify-scoped key can create an accepted unilateral Spool, submit evidence on behalf of its identified external provider, run verification, and receive earned arithmetic plus a signed Outcome Receipt in one response. The provider does not accept conditions in Spoolis. The receipt records this boundary at agreement.acceptance with mode: "unilateral", provider_accepted_in_spoolis: false, and accepted_by: "initiator". External settlement is the default.
{
"conditions": [{
"description": "Every row includes status",
"deterministic_check": { "checker": "completeness", "required_fields": ["status"] }
}],
"max_amount_cents": 3,
"unit": { "total_units": 3, "unit_amount_cents": 1 },
"evidence": {
"type": "dataset",
"rows": [
{ "id": 1, "status": "complete" },
{ "id": 2, "status": "complete" },
{ "id": 3 }
],
"provenance": "api_response"
},
"settlement": "external",
"idempotency_key": "result-123"
}conditions accepts plain text or explicit deterministic checks. The response contains earned arithmetic, the signed receipt, and the verification remediation fields documented below. The same /api/v1 route accepts a sandbox bearer token and dispatches only to sandbox handlers and state. Production-only capabilities return unsupported_environment_capability with a production-key remediation. The /api/sandbox/* tree remains a working compatibility alias.
type OneCallVerifyResponse = {
spool_id: string
earned_cents: number
accepted: number
rejected: number
rejections: Array<{ unit: number; reason: string }>
receipt: OutcomeReceipt
receipt_url: string
verification_run_id: string
terminal: boolean
settlement_eligible: boolean
can_resubmit_evidence: boolean
can_rerun: boolean
next_step: {
type: 'settle' | 'submit_evidence' | 'rerun_allowed' | 'await_confirmation' | 'sign_settlement' | 'terminal' | 'new_spool'
action: 'settle' | 'submit_evidence' | 'rerun_allowed' | 'await_confirmation' | 'sign_settlement' | 'terminal' | 'new_spool'
actor: 'buyer' | 'provider' | 'verifier' | 'none'
reason: string
}
decision_authority: 'deterministic' | 'buyer_confirmation' | 'both_confirm' | 'none'
corrective_hint?: string
}Machine counterparty exchange
The Spool initiator can call POST /api/v1/spools/{id}/invite with a full-scope key. The active Spool must have exactly one unassigned counterparty slot. The response shows a 15-minute, single-use grant token, its expiry, and the exchange URL.
The counterparty sends the grant token to POST /api/v1/keys/exchange as {"grant_token":"spg_..."}. The response shows a counterparty API key once. This key is agreement identity only. It can read its bound Spool, accept, decline, and submit evidence while the outcome is active. It cannot create or list Spools, list events, commit or fund settlement, run verification, or act on another Spool.
Funding and payout setup
Spoolis determines earned value and signs the Outcome Receipt. A wallet, marketplace, payment system, or configured integration may verify and consume it, then act under its own authorization and settlement policy.
The optional built-in path uses a configured settlement adapter. When Stripe Connect is configured for a production human transaction, the provider completes payout onboarding and the buyer opens hosted Checkout. That adapter creates a manual-capture authorization and acts on the verification result according to its policy.
Verification method request values
deterministic and human_confirmed are live. ai_assisted is live for claim_cited. external_tool is live only when a third-party condition declares its one allowed external_judge and proof requirement, followed by POST /api/v1/spools/{id}/external-result.
Availability and your execution path
If your workflow gates a payment or a next action on a fresh verification, Spoolis is part of your execution path, the same way CI or a payment processor is. Plan for that honestly:
- Set a timeout and a fallback policy. Decide before integrating what your system does when a verification call fails or times out: hold the action, retry later, or proceed under your own policy. That decision is yours; Spoolis never defaults it for you.
- Retries are safe where marked. Error responses carry a stable
codeand aretryableflag; branch on those, not the HTTP status. Duplicate canonical external-result submissions are idempotent, and settlement records are keyed so a replay can never double-count. - Consuming an existing Outcome never depends on us. The receipt is self-contained and verifies offline with the published verifier and pinned public keys. Downstream systems re-checking, auditing, or acting on an already-issued Outcome have no runtime dependency on Spoolis.
- Re-reads never recompute. A persisted result is computed once and rendered from storage, so the answer you act on cannot drift between reads.
Monitor /api/health for liveness.
Troubleshooting by stage
These names come from the REST error registry, receipt verifier, and MCP client. Always branch on the stable code or reason, not the HTTP status alone.
| Stage | Actual code or reason | Likely cause | Next action |
|---|---|---|---|
| Session | session_expired, limit_reached, sandbox_resting, rate_limited, configuration_unavailable | The session expired, exhausted a budget, the shared sandbox is resting, rate is too high, or required configuration is unavailable. | Mint a fresh session for expired or exhausted sessions. Otherwise inspect retryable and remediation, then wait before retrying. |
| Compile | invalid_json, input_too_large, ambiguity_required, validation_failed, compile_failed | The body is malformed, too large, missing an exact amount, fails a field rule, or cannot be compiled. | Correct the named field or intent. Retry compile_failed only when retryable is true. |
| Lifecycle | unauthenticated, forbidden, spool_not_found, unknown_action, invalid_state, store_conflict | Authentication, visibility, action name, lifecycle state, or a concurrent write blocked the request. | Fix authentication or the action. Read the current Spool before retrying a conflict. |
| Settlement | settlement_unavailable | The requested managed settlement path is unavailable. | Follow remediation or use an independently authorized external consumer. |
| Wallet settlement | production settlement access is pilot-gated | The acting account is not enabled for production settlement. | Request production enablement for the account, then retry commit. |
| Wallet settlement | no active production principal | The account has no active principal on the configured production network. | Register the principal in Settings, Wallet, then retry commit. |
| Wallet settlement | No spend permission grants Spoolis authority | No matching USDC permission exists for the registered principal, configured spender, and network. | Create the permission from the guided snippet in Settings, Wallet, then verify status. |
| Wallet settlement | expired or not yet active | Every matching permission is outside its active time window. | Create a new permission if the prior one expired, or wait until its onchain start time. |
| Wallet settlement | Every spend permission for the principal account is revoked | Every matching active-time permission is revoked in stored or onchain state. | Create a new permission with your principal credentials, then verify status. |
| Wallet settlement | exceeds the remaining spend permission allowance | The earned amount is greater than the authority left in the current period. | Wait for the next permission period or create a suitable bounded permission before retrying. |
| Wallet settlement | CDP wallet settlement is not enabled | The production wallet lane is not enabled in this deployment. | Do not retry until Spoolis reports that production wallet settlement is available. |
| Attestation | trusted_key_unavailable, internal_error | Trust configuration or the service prevented verification. | Retry only when retryable is true. Do not treat the artifact as verified. |
| Receipt verifier | unknown_schema, environment_mismatch, invalid_evidence_root, invalid_run_digest, invalid_agreement_hash, receipt_id_mismatch, earned_exceeds_committed, earned_amount_mismatch, aggregation_inconsistent | The receipt conflicts with the supported schema, caller environment, digests, ID, amounts, or aggregation rule. | Reject the receipt and inspect the named invariant. |
| Receipt verifier | untrusted_signing_key, signing_key_id_mismatch, invalid_signature, malformed_receipt | The key is not trusted, the key ID differs, the signature fails, or the receipt cannot be parsed. | Reject the receipt. Refresh only from reviewed trust material. |
| Online status | corrected, revoked | An operator marked the authentic receipt corrected or revoked. | Treat verifier policy as invalid and follow the advisory under your reconciliation policy. No payment is automatically reversed. |
| Receipt advisories | status_source_unavailable, key_retiring | No online status was supplied, or the trusted key is retiring. | Continue offline-only under caller policy, or review and pin the current published trust entry. |
| MCP | production_key_required, sandbox_session_failed | The tool has no sandbox equivalent, or the MCP server could not mint its demo session. | Configure SPOOLIS_API_KEY for a production-only tool, or mint a fresh sandbox session and retry. |
Payment path selection
Payment path selection is fail closed. It considers the actor, amount, currency, configured adapters, declared machine capabilities, readiness, and demo mode. It returns an explicit refusal when no compatible production path qualifies. See Payment paths for the complete model.
Create a Spool
POST /api/v1/spools
Decide who does the work before you create. A person or business provider is paid through payout setup. An agent or software provider (type: "agent_task") is paid through the agent settlement path with wallet authority. The 201 response states the selected path in its lane block, and for a bilateral Spool it includes share_url plus a handoff block that says how the counterparty receives the Spool and whether an invite email was sent.
Returns the created canonical Spool with HTTP 201. The request is strict: unknown fields are rejected. This example represents a buyer's $100.00 maximum for 100 enrichment records at $1.00 per accepted record.
{
"type": "agent_task",
"parties": [
{ "role": "buyer", "kind": "agent", "display_name": "Buyer agent" },
{ "role": "provider", "kind": "service", "display_name": "Data provider" }
],
"value": { "amount_cents": 10000, "currency": "USD" },
"deliverable": {
"type": "dataset", "title": "Enrichment records",
"description": "100 enrichment records", "attributes": {}
},
"terms": [{
"text": "100 enrichment records at $1.00 per accepted record",
"provenance": { "source": "user_added", "origin": "user_added" }
}],
"conditions": [{
"description": "Exactly 100 rows", "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"] }
}]
}Request fields
agreement: optional mode. Omit it or usebilateralfor the existing two-party lifecycle. Useunilateralwhen only the initiator binds and the seller or provider is external.type:goods_in_person,service,digital, oragent_task. Defaults toservice. This field selects the provider path:agent_taskmeans an agent or software provider paid through the agent settlement path with wallet authority; every other value means a person or business provider who must finish payout setup before the buyer can fund. The responselaneblock names the selected path and flags a defaultedtype.parties: exactly two unique display names. Roles arebuyer,seller,client, orprovider; kinds arehuman,agent,org, orservice. Optional fields arecontact,account_id, and a Stripe-stylestripe_account_id.value: positive integeramount_cents, literal currencyUSD, and optionalrail_preference.deliverable: requiredtype,title,description, and string-to-stringattributes.terms: at least one item withtextand provenance. Provenance source islisting,chat,image,freeform, oruser_added.conditions: an array with description, required flag, and verification method. Deterministic conditions must declare one checker:row_count,completeness,duplicate_rate,url_format,json_path,http_status,text_contains,hash_matches, ordeadline_met. AI-assisted conditions must declareclaim_citedwith a boundedclaimand optionalevidence_ref.row_countrequires exactly the declared count.url_formatvalidates URL format only and does not fetch the network; usehttp_statusor reachability wording for explicit reachability.economics: optionalfee_centsand ISO datetimeexpiry.
Read a Spool
GET /api/v1/spools/{id}
Returns the canonical Spool. The economics.allowed_actions array is computed for the authenticated party. A share ID may also resolve here.
Every Spool read and verification response includes terminal, settlement_eligible, can_resubmit_evidence, can_rerun, next_step, and decision_authority. next_step.type is the current field. next_step.action carries the same value for one v1 compatibility period. Evidence and verify appear in allowed_actions only when the matching capability field is true.
Agent Spools carry a persisted pricing_quote: work_budget_cents, spoolis_fee with estimated_cents and maximum_cents, rail_fee marked pass_through, maximum_all_in_cents, expires_at, a quote_digest, payer, and discounts.
GET /api/v1/spools/{id}/events
Returns the canonical event array in recorded order.
Amend a proposed Spool
POST /api/v1/spools/{id}/amend
A compiled or created Spool in the proposed state is a draft with no economic commitment. The creator's full-scope key can amend it before acceptance: add_conditions takes full condition objects including deterministic checks, remove_condition_ids removes existing conditions, and unitization replaces the per-unit economics. At least one field is required, and the unitization must reconcile with the agreed amount. The response returns the updated Spool with a fresh verification_summary, and the semantic diff is recorded as a spool.amended event. Any state other than proposed with uncommitted settlement returns HTTP 409 invalid_state: acceptance freezes the contract, so post-acceptance amendment is not supported.
The REST API is the full-control path. For agents, the MCP server is the recommended default and exposes the same conceptual lifecycle, including amend_spool.
Quote and accept an agent Spool
POST /api/v1/spools/{id}/quote
The initiator requests a canonical quote for the current compiled plan and economics. The response contains the full persisted quote plus expired and superseded. Request this endpoint again after a compile change to persist a fresh quote. An accepted quote is immutable.
GET /api/v1/spools/{id}/quote
Either party can inspect the current quote. A true superseded value means the compiled plan or economics no longer matches its quote_digest. A true expired value means a fresh quote is required.
POST /api/v1/spools/{id}/accept
For an agent Spool, send { "quote_digest": "sha256:..." }. Acceptance freezes that exact digest. A different or superseded digest returns HTTP 409 invalid_state with reason requote_required. An expired quote returns 409 with reason quote_expired. Inspect or request a fresh quote before retrying.
Outcome settlement
These endpoints require the buyer's full-scope API key. GET /api/v1/spools/{id}/settlement/grant/template returns the exact per-Spool SpendPermission payload. POST /api/v1/spools/{id}/settlement/grant accepts that complete struct with either its signature or an existing approval transaction hash. GET /api/v1/spools/{id}/settlement/grant returns the full stored struct and independent revocation link. Direct-mode buyers initially send both recorded EIP-3009 signatures to POST /api/v1/spools/{id}/settlement/signatures. If the seller leg settles and the fee authorization expires, next_step.required_legs requests only a fresh fee signature. The seller leg is never sent again. This rail is live on Base mainnet; availability depends on the account and configured capabilities.
Read account usage
GET /api/v1/usage
Returns the same account-scoped aggregate shown in Insights. The optional period_days query parameter defaults to 30 and accepts integers from 1 through 365. Full-scope and verify-scoped account keys can read usage. Counterparty keys cannot read account-wide usage.
The response has days, totals, use_cases, and recent. Totals include totalRuns, accepted, submitted, earned_micros, fees_accrued_micros, cogs_micros, and pass_rate. earned_micros is value determined by Spoolis, not settled money. Accrued fees are amounts recorded as finalized in the billing ledger, not necessarily amounts collected. Every field ending in _micros is an exact decimal string.
Read and verify an Outcome Receipt
Continue the canonical example by verifying the delivered records. Spoolis accepts 98 records, rejects 2 with reasons, and returns a signed Outcome Receipt recording $98.00 earned. The canonical recipe is: compile -> verify -> Outcome Receipt -> verifyReceipt -> optional status check -> consumer acts on earned.
Verify the receipt with @spoolis/receipt-verifier and pinned receipt keys from the public trust set. Offline signature verification proves authenticity without Spoolis uptime. The status check is optional and only adds correction, revocation, or advisory state. Call GET /api/v1/receipts/{receipt_id}/status. For known receipts, the response includes state for coarse currentness, status for receipt disposition, and consumptions_recorded when the consumption log is available. A well-formed unknown receipt ID returns HTTP 200 with state: "unknown".
curl -sS https://spoolis.com/api/v1/receipts/ocr_0123456789abcdef01234567/statusVerify the underlying attestation
POST /api/v1/attestations/verify
The attestation is the lower-level in-band signed verdict underneath the Outcome Receipt. Send the complete attestation JSON. This public endpoint checks the environment-specific trusted key, signature, digests, expiry, and amount bounds. It returns valid, environment, and machine-readable reasons. It never returns the submitted attestation.
Actions
Send POST /api/v1/spools/{id}/{action}. Lifecycle rules can reject actions that are unavailable in the current state.
| Action | Request body | Behavior |
|---|---|---|
propose | No body | Move agreement to proposed. |
accept | No body | Accept a proposed agreement. |
decline | No body | Decline and cancel the outcome. |
cancel | No body | Cancel the agreement and outcome. |
abandon | No body | Let either party cancel an active Spool before settlement is committed. Requires a full-scope key. |
commit | `payment_method?: string` beginning `pm_`; settlement fields `direct_signer?: principal_runtime` and `settlement_mode?: direct_eip3009 | unattended_outcome_settlement` | Ask the configured settlement adapter to authorize and, where supported, hold. |
evidence | `condition_id`, `type`, `source`, optional `metadata`, `on_behalf_of`, `provenance`, and `claim_class` | Append evidence before verification or after a non-terminal partial, uncertain, or failed result. Each evidence item is capped at 1 MB; larger material should be referenced by URL or digest. |
external-result | `evaluator`, `verdict`, `condition_results`, `proof`, `observed_at`, and optional `evidence_refs` | Store a proof-bound judgment for the matching external judge declared by the agreement. Duplicate canonical submissions are idempotent. |
verify | Optional `confirmations` array | Run or re-run verification over the full evidence set and, when signing is configured, return an Outcome Receipt with its underlying attestation. |
complete | No body | Read a Spool whose outcome is already completed. This action does not run verification or settlement. |
onboard-provider | `refresh_url`, `return_url` | Start provider onboarding only when the adapter supports it. |
Evidence type is one of photo, text, file, url, dataset, or confirmation. Optional claim_class is actor_identity, artifact_state, or machine_fact. claim_class says what kind of claim the evidence supports, not how trustworthy the evidence is. Each verify confirmation contains condition_id, method buyer_confirm or both_confirm, outcome passed, failed, or waived, an evidence_refs array, and optional detail.
Direct x402 pay-per-verification
Credits are optional. Direct x402 verification requires no account, API key, or prepaid balance. The paid retry returns the signed Outcome Receipt in the same call.
# 1. The unpaid call returns 402 with an x402 V2 challenge for Base mainnet USDC.
curl -X POST https://spoolis.com/api/v1/verify/x402 \
-H "Content-Type: application/json" \
-d @one-shot-verify.json
# 2. Sign the advertised payment requirement with your wallet, then retry
# with the payment-signature header. The 201 response includes the
# signed Outcome Receipt and a PAYMENT-RESPONSE header.
curl -X POST https://spoolis.com/api/v1/verify/x402 \
-H "Content-Type: application/json" \
-H "payment-signature: $X402_PAYMENT" \
-d @one-shot-verify.jsonWhen you receive 402 credits_required
The response gives the exact quote, remaining allowance, available usage balance, required amount, and supported payment methods. Stripe Checkout stays first. An agent can buy a $5, $10, or $20 pack through MPP or x402. The MPP steps below apply only when that lane is enabled. The x402 credit-pack lane is live for Base mainnet USDC.
{
"code": "credits_required",
"retryable": true,
"retry_after": "reload",
"quote": { "currency": "usd", "compile": null, "verification": { "pricing_version": "verification_intro_v2", "amount_micros": 20000 }, "maximum_micros": 20000, "amount_due_micros": 20000 },
"balance": { "available_micros": 0 },
"allowance": { "period_start": "2026-08-01", "period_end": "2026-09-01", "verification": { "limit": 10, "used": 10, "remaining": 0 }, "compile": { "limit": 10, "used": 0, "remaining": 10 } },
"included_remaining": { "verification_runs": 0, "free_text_compiles": 10, "resets_on": "2026-09-01" },
"available_balance_micros": 0,
"available_balance_usd": "0.0000",
"required_for_request_micros": 20000,
"required_for_request_usd": "0.0200",
"minimum_reload_cents": 500,
"payment_methods": [
{ "type": "stripe_checkout", "authorization": "account_owner", "url": "https://spoolis.com/settings?tab=billing&topup=500" },
{ "type": "mpp", "authorization": "agent_credential", "url": "https://spoolis.com/api/v1/credits/packs/mpp", "packs_cents": [500, 1000, 2000], "methods": ["card"], "protocol": "https://mpp.dev" },
{ "type": "x402", "authorization": "agent_wallet", "url": "https://spoolis.com/api/v1/credits/packs/x402", "packs_cents": [500, 1000, 2000], "chains": ["base"], "asset": "usdc", "network": "eip155:8453", "facilitator": "hosted", "protocol": "https://x402.org", "x402_version": 2 }
],
"top_up_url": "https://spoolis.com/settings?tab=billing"
}x402 credit-pack loop
Send an authenticated POST /api/v1/credits/packs/x402. A 402 response carries an x402 V2 challenge in PAYMENT-REQUIRED. Decode it and sign one accepted Base mainnet USDC requirement as an EIP-3009 TransferWithAuthorization. Use the token's advertised live EIP-712 domain, USD Coin with version 2.
Retry the same request with the Spoolis key in X-Api-Key and the signed x402 V2 payload in PAYMENT-SIGNATURE. A 202 response with status: payment_accepted means the facilitator confirmed settlement. Poll GET /api/v1/credits/balance before retrying the original operation. The usage balance updates when the facilitator confirms settlement, usually within seconds.
# 1. Request the $5 pack. Decode the PAYMENT-REQUIRED header from this 402.
curl -i https://spoolis.com/api/v1/credits/packs/x402 \
-H "Authorization: Bearer $SPOOLIS_API_KEY" \
-H "Content-Type: application/json" \
--data '{"pack_cents":500}'
# 2. Sign one accepted requirement, then retry with the x402 V2 payload.
curl -i https://spoolis.com/api/v1/credits/packs/x402 \
-H "X-Api-Key: $SPOOLIS_API_KEY" \
-H "PAYMENT-SIGNATURE: $X402_PAYMENT_SIGNATURE" \
-H "Content-Type: application/json" \
--data '{"pack_cents":500}'
# 3. Poll the shared usage balance before retrying the original operation.
curl -sS https://spoolis.com/api/v1/credits/balance \
-H "Authorization: Bearer $SPOOLIS_API_KEY"MPP credit-pack loop
Keep the Spoolis API key in the Bearer scheme. On the paid retry, add the MPP credential as a second, comma-separated Authorization scheme. The 202 response means Stripe accepted the payment, not that the ledger is already credited. Poll the balance before retrying the original verification request.
# 1. The original call returns 402 credits_required with the MPP pack URL.
curl -i https://spoolis.com/api/v1/verify \
-H "Authorization: Bearer $SPOOLIS_API_KEY" \
-H "Content-Type: application/json" \
--data @verify-request.json
# 2. Request the $5 pack. Save the WWW-Authenticate challenge from this 402.
curl -i https://spoolis.com/api/v1/credits/packs/mpp \
-H "Authorization: Bearer $SPOOLIS_API_KEY" \
-H "Content-Type: application/json" \
--data '{"pack_cents":500}'
# 3. Let the agent wallet pay the challenge, then retry. Send the Spoolis key
# as X-Api-Key so the wallet client can own the Authorization header.
curl -i https://spoolis.com/api/v1/credits/packs/mpp \
-H "X-Api-Key: $SPOOLIS_API_KEY" \
-H "Authorization: Payment $MPP_CREDENTIAL" \
-H "Content-Type: application/json" \
--data '{"pack_cents":500}'
# 4. Poll until the webhook-confirmed balance includes the purchase.
curl -sS https://spoolis.com/api/v1/credits/balance \
-H "Authorization: Bearer $SPOOLIS_API_KEY"
# 5. Retry the original verification request unchanged.
curl -i https://spoolis.com/api/v1/verify \
-H "Authorization: Bearer $SPOOLIS_API_KEY" \
-H "Content-Type: application/json" \
--data @verify-request.jsonErrors and rate limits
Every v1 and sandbox error keeps the human-readable error and adds a stable code, retryable flag, and actionable remediation. When relevant, field identifies the failed field or condition path and docs links to help.
{"error":"Invalid API key","code":"unauthenticated","retryable":false,"remediation":"Provide a valid Spoolis API key in the Authorization header.","field":"headers.authorization","docs":"https://spoolis.com/docs/api"}| Code | Status | Retryable | Meaning |
|---|---|---|---|
ambiguity_required | 422 | No | The request needs a missing exact value. |
checker_error | 500 | No | A declared checker errored before producing a verification outcome. |
configuration_unavailable | 503 | Yes | A required service configuration is unavailable. |
compile_failed | 422 | Yes | The sandbox compiler could not compile the supplied intent. |
conflict | 409 | No | The requested operation conflicts with the current resource state. |
credits_required | 402 | Yes | The account needs credits for the quoted work; retry after a reload. |
forbidden | 403 | No | The caller cannot access this resource or operation. |
grant_cap_exceeded | 409 | No | The per-Spool grant exceeds a configured OutcomeSettlement exposure cap. |
grants_paused | 503 | Yes | New OutcomeSettlement grants are paused while existing settlement continues. |
input_too_large | 413 | No | The request exceeds a documented size limit. |
evidence_too_large | 413 | No | An evidence item exceeds the documented size limit. |
internal_error | 500 | Yes | The server could not complete the request. |
invalid_json | 400 | No | The request body is not valid JSON. |
invalid_state | 409 | No | The operation is unavailable in the current lifecycle state. |
method_not_allowed | 405 | No | The HTTP method is not supported at this path. |
not_found | 404 | No | The requested feature or resource is not available. |
not_acceptable | 406 | No | The requested representation is not available at this path. |
not_allowed_in_unilateral_mode | 409 | No | The lifecycle action requires a bilateral agreement. |
payout_destination_required | 409 | No | The provider must register a valid payout destination before wallet capture. |
payment_required_mpp | 402 | Yes | An MPP payment credential is required to buy the selected credit pack. |
payment_required_x402 | 402 | Yes | An x402 wallet authorization is required to buy the selected credit pack. |
payment_failed_x402 | 402 | Yes | The x402 payment could not be confirmed by the hosted facilitator. |
limit_reached | 200 | No | The sandbox session has exhausted an operation budget. |
rate_limited | 429 | Yes | The request rate limit was reached. |
recipe_hash_mismatch | 409 | No | The supplied recipe hash does not match the local registry recipe. |
recipe_not_found | 404 | No | The requested recipe does not exist in the local registry. |
route_not_found | 404 | No | No API route exists at this path. |
sandbox_resting | 200 | Yes | The shared daily sandbox budget is resting. |
settlement_unavailable | 409 | Yes | The requested managed settlement path is not currently available. |
session_expired | 401 | Yes | The sandbox session is missing or expired. |
spool_not_found | 404 | No | The requested Spool does not exist or is not visible to the caller. |
store_conflict | 409 | Yes | The Spool changed before the update was saved. |
trusted_key_unavailable | 200 | Yes | The attestation environment trust key is unavailable. |
unsupported_environment_capability | 409 | No | The authenticated environment does not support this capability. |
unknown_action | 404 | No | The requested lifecycle action is not supported. |
unauthenticated | 401 | No | Valid authentication was not provided. |
validation_failed | 400 or 422 | No | A request field or attestation failed validation. |
wrong_funding_lane | 409 | No | The operation requires a wallet-funded Spool. |
The implemented v1 limit is 120 requests per minute per account or test-key party label and forwarded IP. Production requests fail closed when Upstash is not configured.
Rate limit headers
Rate-limited JSON operations return RateLimit-Policy and RateLimit on successful and rejected responses. Rejected requests also return Retry-After in seconds.
RateLimit-Policy: "api-v1";q=120;w=60
RateLimit: "api-v1";r=119;t=42
Retry-After: 42Reviews and disputes
dispute_policy binds the window, scope, freeze, evidence, escalation, and finality rules into the agreement. POST /api/v1/receipts/{receipt_id}/reviews creates a named review request and hosted decision link. Review and dispute resources expose their current state. A confirmation can create a successor with review_decision; a resolved dispute can create one with dispute_resolution. See reviews and disputes for the full lifecycle.
Evidence requirements and unit identity
Each condition may include evidence_requirements with an expected type, source reference, optional provenance or supplier constraints, optional freshness, and optional on_missing. The declaration is covered by agreement_hash. Receipt rows use present, missing, stale, unavailable, or provenance_unsatisfied; unsatisfied required evidence defaults the criterion to uncertain.
Unitization may include identity with a source key and visibility label or pseudonymous. Public unit results carry a salted identity.id; label mode also carries the source value. The per-Spool salt is private and is not returned in the public receipt.
Binding condition rules
Conditions are binding only when the caller explicitly includes them. Compiler suggestions stay separate until a user or caller adds them. Terms and conditions with unresolved placeholders such as [amount], [date], or TBD are rejected with HTTP 422.
Declared gated action
gated_action optionally states what the caller says the Outcome gates: payment, workflow_step, publish, merge, delegation, or other. gated_action_note adds up to 120 characters of caller-declared context. These fields are declared intent, not verified facts, and never affect verdicts, pricing, earned value, or gate behavior.
Finality policy
The optional finality field contains window, reopen, correction, and on_silence. When both finality and dispute_policy are present, they must agree.