Spoolis docs
API reference
The v1 API is a bearer-authenticated Spool resource. Production keys bind created Spools to your account.
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/keys/exchange | None | Production | Exchange an API key grant | Guide |
POST /api/v1/spools | Bearer | Production | Create a Spool | Guide |
GET /api/v1/spools/{id} | Bearer | Production | Get a Spool | 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/receipts/{id}/status | None | Production and demo | Get Outcome Receipt status | Guide |
POST /api/v1/attestations/verify | None | Production and demo | Verify an attestation | Guide |
GET /api/sandbox/session | Bearer | Demo | Get sandbox session state | Guide |
POST /api/sandbox/session | None | Demo | Mint a sandbox session | Guide |
POST /api/sandbox/compile | Bearer | Demo | Compile a sandbox Spool | Guide |
POST /api/sandbox/spools/{id}/{action} | Bearer | Demo | Act on a sandbox Spool | 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.
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 is Live, human_confirmed is Live, external_tool is Reserved, and ai_assisted is Planned. Schema acceptance does not mean an executor is live. Use the single method-status table when choosing a value.
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. |
| 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 and preview boundaries.
Create a Spool
POST /api/v1/spools
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 }
}]
}Request fields
type:goods_in_person,service,digital, oragent_task. Defaults toservice.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.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.
GET /api/v1/spools/{id}/events
Returns the canonical event array in recorded order.
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. Verify that receipt outside the API with @spoolis/receipt-verifier and pinned receipt keys from the public trust set. Offline signature verification is sufficient. You may then call GET /api/receipts/{id}/status for {receipt_id, status: 'active', advisories: [], checked_at}. After verification and the optional status check, the payment stack may act on the $98.00 earned amount under its own authorization policy.
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.
| 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_` | Ask the configured settlement adapter to authorize and, where supported, hold. |
evidence | `condition_id`, `type`, `source`, optional `metadata` | Submit evidence during fulfillment. |
verify | Optional `confirmations` array | Run verification 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. Each verify confirmation contains condition_id, method buyer_confirm or both_confirm, outcome passed, failed, or waived, an evidence_refs array, and optional detail.
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":"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. |
configuration_unavailable | 503 | Yes | A required service configuration is unavailable. |
compile_failed | 422 | Yes | The sandbox compiler could not compile the supplied intent. |
forbidden | 403 | No | The caller cannot access this resource or operation. |
input_too_large | 413 | No | The request exceeds a 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. |
limit_reached | 200 | No | The sandbox session has exhausted an operation budget. |
rate_limited | 429 | Yes | The request rate limit was reached. |
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. |
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. |
The implemented v1 limit is 120 requests per minute per account or test-key party label and forwarded IP. A limited request returns 429 with a Retry-After header. When Upstash is not configured, the application currently allows requests without enforcement and logs a warning, so deployments must not assume the limit is active merely because it is documented here.
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.