SpoolisDocs

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.

request.json
{
  "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"
}
closing readout
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 pathAuthEnvironmentPurposeGuide
POST /api/v1/verifyBearerProductionRun acceptance in one callGuide
POST /api/v1/verify/x402BearerProductionRun acceptance on delivered agent work and return per-unit results, earned value, and a signed Outcome Receipt.Guide
POST /api/v1/keys/exchangeBearerProductionExchange an API key grantGuide
GET /api/v1/usageBearerProductionGet account usageGuide
POST /api/v1/credits/packs/mppBearerProductionBuy a credit pack with MPPGuide
POST /api/v1/credits/packs/x402BearerProductionBuy a credit pack with x402Guide
GET /api/v1/credits/balanceBearerProductionGet credit balanceGuide
POST /api/v1/invoice-auditBearerProductionCompare a CSV or PDF invoice with a record CSVGuide
POST /api/v1/promise-replayBearerProductionReplay a vendor promise against historical outcomesGuide
POST /api/v1/policy-compilerBearerProductionCompile an outcome policyGuide
POST /api/v1/policy-compiler/confirmBearerProductionConfirm an outcome policyGuide
POST /api/v1/spoolsBearerProductionCreate a SpoolGuide
GET /api/v1/spools/{id}BearerProductionGet a SpoolGuide
GET /api/v1/spools/{id}/discrepanciesBearerProductionList discrepanciesGuide
POST /api/v1/spools/{id}/discrepanciesBearerProductionOpen a discrepancyGuide
POST /api/v1/discrepancies/{id}/resolveBearerProductionResolve a discrepancyGuide
POST /api/v1/discrepancies/{id}/dismissBearerProductionDismiss a discrepancyGuide
GET /api/v1/spools/{id}/settlement/grantBearerProductionGet a per-Spool settlement grantGuide
POST /api/v1/spools/{id}/settlement/grantBearerProductionCreate a per-Spool settlement grantGuide
GET /api/v1/spools/{id}/settlement/grant/templateBearerProductionGet a per-Spool grant templateGuide
POST /api/v1/spools/{id}/settlement/signaturesBearerProductionSubmit direct settlement signaturesGuide
GET /api/v1/spools/{id}/quoteBearerProductionInspect an agent quoteGuide
POST /api/v1/spools/{id}/quoteBearerProductionRequest an agent quoteGuide
POST /api/v1/spools/{id}/inviteBearerProductionCreate a counterparty invitationGuide
GET /api/v1/spools/{id}/eventsBearerProductionList Spool eventsGuide
POST /api/v1/spools/{id}/{action}BearerProductionAct on a SpoolGuide
GET /api/v1/receipts/{receipt_id}BearerProduction and demoGet an Outcome ReceiptGuide
POST /api/v1/receipts/{receipt_id}/consumptionsBearerProduction and demoRecord Outcome Receipt consumptionGuide
POST /api/v1/receipts/{receipt_id}/supersedeBearerProduction and demoSupersede an Outcome ReceiptGuide
GET /api/outcome-indexBearerProductionRead the Outcome Index datasetGuide
GET /api/v1/receipts/{receipt_id}/statusBearerProduction and demoGet Outcome Receipt statusGuide
POST /api/v1/receipts/{receipt_id}/reviewsBearerProduction and demoRequest a human reviewGuide
GET /api/v1/reviews/{review_id}BearerProductionGet a review requestGuide
POST /api/v1/reviews/{review_id}/decisionBearerProductionDecide a reviewGuide
POST /api/v1/receipts/{receipt_id}/disputesBearerProduction and demoOpen a disputeGuide
GET /api/v1/disputes/{dispute_id}BearerProductionGet a disputeGuide
POST /api/v1/attestations/verifyBearerProduction and demoVerify an attestationGuide
GET /api/sandbox/sessionBearerDemoGet sandbox session stateGuide
POST /api/sandbox/sessionBearerDemoMint a sandbox sessionGuide
POST /api/sandbox/compileBearerDemoCompile a sandbox SpoolGuide
POST /api/sandbox/verifyBearerDemoVerify a sandbox result in one callGuide
GET /api/sandbox/spools/{id}BearerDemoGet a sandbox Spool compatibility aliasGuide
GET /api/sandbox/spools/{id}/eventsBearerDemoList sandbox Spool events compatibility aliasGuide
POST /api/sandbox/spools/{id}/{action}BearerDemoAct on a sandbox SpoolGuide
GET /api/v1/sourcesBearerProductionList evidence sourcesGuide
POST /api/v1/sourcesBearerProductionAdd an evidence sourceGuide
GET /api/v1/sources/{id}BearerProductionGet an evidence sourceGuide
POST /api/v1/sources/{id}/syncBearerProductionEnqueue an evidence source syncGuide
POST /api/v1/sources/{id}/webhookBearerProductionReceive signed source evidenceGuide
GET /api/v1/spools/{id}/factsBearerProductionList normalized factsGuide
GET /api/v1/spools/{id}/matchesBearerProductionList claim matchesGuide
POST /api/v1/spools/{id}/claimsBearerProductionAdd a vendor-reported claimGuide
POST /api/v1/spools/{id}/matchBearerProductionRun deterministic outcome matchingGuide

Base URL and authentication

request
Authorization: Bearer spk_live_...
Content-Type: application/json

Create 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.

request.json
{
  "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.

response shape
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 code and a retryable flag; 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.

StageActual code or reasonLikely causeNext action
Sessionsession_expired, limit_reached, sandbox_resting, rate_limited, configuration_unavailableThe 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.
Compileinvalid_json, input_too_large, ambiguity_required, validation_failed, compile_failedThe 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.
Lifecycleunauthenticated, forbidden, spool_not_found, unknown_action, invalid_state, store_conflictAuthentication, 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.
Settlementsettlement_unavailableThe requested managed settlement path is unavailable.Follow remediation or use an independently authorized external consumer.
Wallet settlementproduction settlement access is pilot-gatedThe acting account is not enabled for production settlement.Request production enablement for the account, then retry commit.
Wallet settlementno active production principalThe account has no active principal on the configured production network.Register the principal in Settings, Wallet, then retry commit.
Wallet settlementNo spend permission grants Spoolis authorityNo 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 settlementexpired or not yet activeEvery 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 settlementEvery spend permission for the principal account is revokedEvery matching active-time permission is revoked in stored or onchain state.Create a new permission with your principal credentials, then verify status.
Wallet settlementexceeds the remaining spend permission allowanceThe 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 settlementCDP wallet settlement is not enabledThe production wallet lane is not enabled in this deployment.Do not retry until Spoolis reports that production wallet settlement is available.
Attestationtrusted_key_unavailable, internal_errorTrust configuration or the service prevented verification.Retry only when retryable is true. Do not treat the artifact as verified.
Receipt verifierunknown_schema, environment_mismatch, invalid_evidence_root, invalid_run_digest, invalid_agreement_hash, receipt_id_mismatch, earned_exceeds_committed, earned_amount_mismatch, aggregation_inconsistentThe receipt conflicts with the supported schema, caller environment, digests, ID, amounts, or aggregation rule.Reject the receipt and inspect the named invariant.
Receipt verifieruntrusted_signing_key, signing_key_id_mismatch, invalid_signature, malformed_receiptThe 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 statuscorrected, revokedAn 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 advisoriesstatus_source_unavailable, key_retiringNo 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.
MCPproduction_key_required, sandbox_session_failedThe 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.

request.json
{
  "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 use bilateral for the existing two-party lifecycle. Use unilateral when only the initiator binds and the seller or provider is external.
  • type: goods_in_person, service, digital, or agent_task. Defaults to service. This field selects the provider path: agent_task means 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 response lane block names the selected path and flags a defaulted type.
  • parties: exactly two unique display names. Roles are buyer, seller, client, or provider; kinds are human, agent, org, or service. Optional fields are contact, account_id, and a Stripe-style stripe_account_id.
  • value: positive integer amount_cents, literal currency USD, and optional rail_preference.
  • deliverable: required type, title, description, and string-to-string attributes.
  • terms: at least one item with text and provenance. Provenance source is listing, chat, image, freeform, or user_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, or deadline_met. AI-assisted conditions must declare claim_cited with a bounded claim and optional evidence_ref. row_count requires exactly the declared count. url_format validates URL format only and does not fetch the network; use http_status or reachability wording for explicit reachability.
  • economics: optional fee_cents and ISO datetime expiry.

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".

terminal
curl -sS https://spoolis.com/api/v1/receipts/ocr_0123456789abcdef01234567/status

Verify 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.

ActionRequest bodyBehavior
proposeNo bodyMove agreement to proposed.
acceptNo bodyAccept a proposed agreement.
declineNo bodyDecline and cancel the outcome.
cancelNo bodyCancel the agreement and outcome.
abandonNo bodyLet 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.
verifyOptional `confirmations` arrayRun or re-run verification over the full evidence set and, when signing is configured, return an Outcome Receipt with its underlying attestation.
completeNo bodyRead 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.

terminal
# 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.json

When 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.

credits-required.json
{
  "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.

terminal
# 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.

terminal
# 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.json

Errors 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.json
{"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"}
CodeStatusRetryableMeaning
ambiguity_required422NoThe request needs a missing exact value.
checker_error500NoA declared checker errored before producing a verification outcome.
configuration_unavailable503YesA required service configuration is unavailable.
compile_failed422YesThe sandbox compiler could not compile the supplied intent.
conflict409NoThe requested operation conflicts with the current resource state.
credits_required402YesThe account needs credits for the quoted work; retry after a reload.
forbidden403NoThe caller cannot access this resource or operation.
grant_cap_exceeded409NoThe per-Spool grant exceeds a configured OutcomeSettlement exposure cap.
grants_paused503YesNew OutcomeSettlement grants are paused while existing settlement continues.
input_too_large413NoThe request exceeds a documented size limit.
evidence_too_large413NoAn evidence item exceeds the documented size limit.
internal_error500YesThe server could not complete the request.
invalid_json400NoThe request body is not valid JSON.
invalid_state409NoThe operation is unavailable in the current lifecycle state.
method_not_allowed405NoThe HTTP method is not supported at this path.
not_found404NoThe requested feature or resource is not available.
not_acceptable406NoThe requested representation is not available at this path.
not_allowed_in_unilateral_mode409NoThe lifecycle action requires a bilateral agreement.
payout_destination_required409NoThe provider must register a valid payout destination before wallet capture.
payment_required_mpp402YesAn MPP payment credential is required to buy the selected credit pack.
payment_required_x402402YesAn x402 wallet authorization is required to buy the selected credit pack.
payment_failed_x402402YesThe x402 payment could not be confirmed by the hosted facilitator.
limit_reached200NoThe sandbox session has exhausted an operation budget.
rate_limited429YesThe request rate limit was reached.
recipe_hash_mismatch409NoThe supplied recipe hash does not match the local registry recipe.
recipe_not_found404NoThe requested recipe does not exist in the local registry.
route_not_found404NoNo API route exists at this path.
sandbox_resting200YesThe shared daily sandbox budget is resting.
settlement_unavailable409YesThe requested managed settlement path is not currently available.
session_expired401YesThe sandbox session is missing or expired.
spool_not_found404NoThe requested Spool does not exist or is not visible to the caller.
store_conflict409YesThe Spool changed before the update was saved.
trusted_key_unavailable200YesThe attestation environment trust key is unavailable.
unsupported_environment_capability409NoThe authenticated environment does not support this capability.
unknown_action404NoThe requested lifecycle action is not supported.
unauthenticated401NoValid authentication was not provided.
validation_failed400 or 422NoA request field or attestation failed validation.
wrong_funding_lane409NoThe 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.

response headers
RateLimit-Policy: "api-v1";q=120;w=60
RateLimit: "api-v1";r=119;t=42
Retry-After: 42

Reviews 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.

API reference · Spoolis