Verify a Spoolis outcome receipt
import { verifyOutcomeReceipt } from '@spoolis/receipt-verifier'
const result = await verifyOutcomeReceipt(receipt, { trustSet })
console.log(result.valid)
console.log(receipt.amounts.earned)
Install
npm install @spoolis/receipt-verifier
No Spoolis account is required to verify a receipt. The package has no runtime dependencies and uses WebCrypto in Node 20 or later, browsers, workers, and agent sandboxes.
Trust material
Spoolis publishes receipt trust material at https://spoolis.com/.well-known/spoolis-keys.json. Pin the receipt entries you accept and pass that array as trustSet for offline verification. fetchTrustSet(url, environment) is an online convenience that returns only the selected environment's receipt trust entries. The environment defaults to production; pass demo explicitly for sandbox receipts. Fetching at verification time is not required or preferred for a pinned deployment.
Offline verification and online status
Signature validity is offline and permanent when a verifier has the receipt and a pinned trust set. It does not depend on Spoolis uptime. Online status is optional:
import { fetchReceiptStatus, verifyOutcomeReceipt } from '@spoolis/receipt-verifier'
const offline = await verifyOutcomeReceipt(receipt, { trustSet })
if (!offline.valid) throw new Error(offline.reasons.join(', '))
const statusSource = await fetchReceiptStatus('https://spoolis.com', receipt.id)
const result = await verifyOutcomeReceipt(receipt, { trustSet, statusSource })
GET /api/receipts/{id}/status currently returns active for a stored receipt. The response reserves corrected, revoked, advisory entries, and superseding_receipt_id for future workflows. Without a status source, verification stays offline-only and includes the status_source_unavailable advisory. A receipt can remain cryptographically valid even if a future online status marks it superseded.
Reasons
unknown_schema | The receipt schema version is not supported. |
environment_mismatch | The receipt environment does not match the requested environment. |
invalid_evidence_root | The evidence root is not a lowercase SHA-256 digest. |
invalid_run_digest | The verification run digest is malformed. |
invalid_agreement_hash | The agreement hash is malformed. |
receipt_id_mismatch | The content-derived receipt ID does not match the payload. |
earned_amount_mismatch | Per-unit arithmetic does not equal the earned amount. |
aggregation_inconsistent | A recognized aggregation policy does not produce the stated result. |
corrected | The online status source marks this receipt corrected. |
revoked | The online status source marks this receipt revoked. |
untrusted_signing_key | No recognized, non-revoked trust entry matches the signing key. |
signing_key_id_mismatch | The trust entry public key does not hash to the signing key ID. |
invalid_signature | The Ed25519 signature does not verify. |
malformed_receipt | A required structure or value is malformed. |
Advisories
unknown_aggregation_policy | The verifier does not know how to recompute this policy. |
past_execute_by | The optional execution deadline has passed. This does not expire the receipt. |
status_source_unavailable | No corrected or revoked status source was supplied. |
key_retiring | The receipt uses a trusted key marked as retiring or retired. |
Security model
A valid result proves that the receipt payload was signed by a key in the supplied trust set. It proves that the content-derived ID matches the signed content. It checks digest formats, recognized aggregation rules, and unit arithmetic. It also enforces the selected production or demo environment. Verification does not prove that referenced evidence is true or available. It does not resolve actor identities. It does not grant payment authority. It does not prove authorization, capture, settlement, or finality on any payment rail. Consumers must enforce their own authorization and consume-once rules before taking economic action. Pin trust material through a trusted channel and review key rotation policy. Use an online status source when corrected or revoked state matters.
Golden vectors
The conformance corpus is maintained at tests/fixtures/receipt-vectors/ in the Spoolis repository. The package test runs every declared vector against the standalone implementation.