The public catalog contains 100 note designs with authentic Nouns #0–99. IDs nm100-000 through nm100-099 identify this source set. The earlier ten designs and generated visual studies belong to separate sets.
The collection and profile companion save which designs you like. Signed-in collection uses your PointCast account; anonymous collection stays in your browser. Neither creates token ownership, a funded wallet or a redeemable balance.
ART FIRST / TEST ONLY
Nouns Money remains collectible art for voluntary art trades, never sold for cash. Demo note counts have no monetary value. There is no live checkout, issuer commitment, backing asset, cash redemption, merchant network, investment return or promised airdrop.
Interface
Runs where
What it does
Catalog HTTP API
Public PointCast
Read the source-100 art metadata.
Collection HTTP API
Same-origin, signed-in session
Read, collect and remove preferences in the account.
TypeScript sandbox
Local browser or Node process
Create, confirm, cancel and retrieve demo intents.
Developer dashboard
This browser
Inspect local sandbox activity and demo receipts.
There are no HTTP payment-intent endpoints, developer API keys or webhook delivery in this release. The OpenAPI document describes the two HTTP paths that exist; sandbox methods have their own local contract below.
The checked-in quickstart script also asserts replay, stable receipts and cancellation. Run node docs/nouns-money/quickstart.mjs.
Browser module · IndexedDB
Use this inside a <script type="module"> on PointCast. The browser ES module needs no build step. For a TypeScript app in the checkout, import packages/nouns-money-sdk/src/index.ts through your bundler. Demo state belongs to that browser origin.
import { NounsMoneySandbox } from
'/nouns-money/sdk.js';
// Same-origin browser ES module; no build step required.
// The default store is IndexedDB, on your app's origin.
const sandbox = new NounsMoneySandbox();
const intent = await sandbox.createIntent(
{ label: 'One-note art demo', noteCount: 1, mode: 'test' },
{ idempotencyKey: 'browser:create:0001' },
);
const result = await sandbox.confirmIntent(
intent.id,
{ noteIds: ['nm100-000'], mode: 'test' },
{ idempotencyKey: 'browser:confirm:0001' },
);
console.log(result.receipt);
Changing origin or database creates a different sandbox. Browser storage can be cleared or blocked. These receipts are local demo records, not trusted payment evidence.
Want to try the complete flow without a development checkout? Open Pay with Nouns Money, create a test intent, select demo notes, confirm and inspect the receipt in the dashboard.
03 / COLLECTION HTTP API
Real paths. Clear authentication.
Read the public catalog from any HTTP client: GET https://pointcast.xyz/nouns-money/catalog.json. For example, run curl --fail https://pointcast.xyz/nouns-money/catalog.json. The response includes schema, mode: collectible-art, count: 100, notes and a provenance path. Each note has id, name, nounId, image and svg.
Method & path
Body
Result
GET /api/me/nouns-money
None
Current signed-in account collection.
POST /api/me/nouns-money
{"noteId":"nm100-000"}
Add one design to the set.
DELETE /api/me/nouns-money
{"noteId":"nm100-000"}
Remove one design from the set.
Successful requests return HTTP 200 with ok: true, schema: pointcast.nouns-money.collection/v1, userId, storage: account, noteIds, collectedAt and updatedAt. Mutation responses include changed. An empty account can have updatedAt: null.
Mutations accept JSON containing exactly one known noteId, at most 256 bytes. Send Content-Type: application/json. The total shared account-state document is capped at 16 KiB; a collect that exceeds it returns payload-too-large. The existing signed, HttpOnly pc_session cookie identifies the account; mutation requests must have an Origin that exactly matches the request origin. Browsers set Origin. Mutations also require X-PointCast-User matching the userId from the displayed GET response. This consistency guard prevents a changed browser session from writing under a different displayed account; it is not an authentication credential. Missing or mismatched values return account-changed (409). There is no cross-origin developer write access.
Use fetch in the signed-in browser
// Run in a signed-in browser on pointcast.xyz.
const currentResponse = await fetch('/api/me/nouns-money');
const current = await currentResponse.json();
if (!currentResponse.ok) throw new Error(current.reason);
const response = await fetch('/api/me/nouns-money', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-PointCast-User': current.userId,
},
body: JSON.stringify({ noteId: 'nm100-000' }),
});
const collection = await response.json();
if (!response.ok) throw new Error(collection.reason);
console.log(collection.storage, collection.noteIds);
// account [ ... ]
// To remove the same note: method: 'DELETE', same JSON body.
The client wraps the catalog and collection paths above. Authentication still comes from the same-origin session. A different base URL does not grant authentication or CORS access.
NounsMoneySandbox runs local methods and stores local records. mode: 'test' is required for creation and confirmation. A request for live mode fails. Confirming demo notes does not consume, move, lock or debit any collected art.
Label: trimmed 1–80 characters, no control characters. Integer noteCount: 1–5. mode: test. Returns requires_notes.
confirmIntent(id, input, options)
Exactly noteCount unique, valid source-100 note IDs. mode: test. Returns succeeded with a stable demo receipt.
cancelIntent(id, options)
Transitions requires_notes to canceled. A succeeded intent cannot be canceled.
retrieveIntent(id)
Read one intent from the current store; unknown IDs fail.
listIntents()
Read the intents in the current store.
Intent IDs begin nmpi_ and use a UUID. Receipt IDs begin nmr_ and derive from that intent UUID. A succeeded intent contains a receipt with id, intentId, mode: test, label, noteCount, sorted noteIds and issuedAt. Dates use ISO 8601. A canceled intent has no receipt.
The receipt records the selected design references in a demo. It is neither a cryptographic receipt nor server-verified settlement. It grants no right to goods, cash, backing assets, tokens or later allocations.
05 / IDEMPOTENCY
Retry the action, keep the result.
Each sandbox mutation requires { idempotencyKey: "your-key-here-0001" }. Keys must contain 16–128 ASCII letters, digits, periods, underscores, colons or hyphens. Generate one key for a deliberate action and reuse it when retrying that action.
Same key, same normalized operation and payload: returns the original result. A retried create produces the same intent ID and original creation snapshot. Use retrieveIntent to inspect the intent’s current state after later actions.
Same key, another operation or payload: fails with idempotency_conflict. Keys share one namespace within the store.
Repeated confirmation with the same note selection: preserves the receipt, even with a new key. Note order is normalized; IDs are unique and sorted.
A terminal intent: cannot change to another terminal outcome or accept a different note selection. Inspect its state instead of assuming a retry can undo it.
The browser store uses IndexedDB transactions to serialize mutations across tabs using the same origin and database. The memory store protects operations using the same store instance. Neither coordinates a different device or a different browser database.
Collection HTTP mutations use set membership instead of an Idempotency-Key header. Collecting an already collected design or removing an absent design succeeds with changed: false. Removing then recollecting records a new collection timestamp. There is no quantity or balance to increase through repeated clicks.
06 / ERRORS
Handle failure before showing success.
Collection API failures return {"ok":false,"reason":"code"} with the relevant non-200 status. The collection SDK client reports API failures as api_error, with status and reason when a server response exists, and malformed responses as invalid_response.
Status
Reason
Action
401
unauthorized
Sign in through PointCast, then retry.
403
origin-not-allowed
Use the same-origin browser. Mutation Origin must exactly match.
415
json-required
Send Content-Type: application/json.
413
body-too-large
Keep the request body at most 256 bytes.
413
payload-too-large
The shared account-state document would exceed 16 KiB. Remove unneeded saved data before retrying.
409
collection-write-conflict
Refresh the account collection and retry the intended action.
409
account-changed
Refresh the displayed account; the expected user ID is missing or differs from the session.
400
bad-body
Send valid JSON.
400
invalid-note-id
Send exactly one noteId field, using nm100-000 through nm100-099.
503
nouns-money-collection-unavailable
Account storage is unavailable. Retry later; do not show success.
Sandbox errors
invalid_request
Fix the label, note count, unique note IDs or idempotency key.
invalid_mode
Only mode: test is supported.
not_found
Retrieve an intent ID created in this store.
invalid_state
The terminal intent cannot accept that transition.
idempotency_conflict
A key was reused for another operation or payload.
capacity_exceeded
The store is full. Preserve needed receipts before an intentional reset.
storage_unavailable
IndexedDB is unavailable. Use an explicit memory store for temporary demos.
corrupted_state
The stored state failed validation. Do not treat it as a valid receipt.
NounsMoneyError exposes code and message. A retryable storage error does not mean an intent succeeded. Retrieve a known intent after recovery; an idempotency replay can establish the result of a known action.
07 / STORAGE & AUTHENTICATION
Three records, three lifetimes.
Record
Lifetime & boundary
Reload / switching
Signed-in art collection
Stored against the authenticated PointCast account.
Reload fetches that account’s set. Switching accounts loads the other account; signing out ends account access.
Anonymous art collection
Device/browser-local preferences, with temporary memory if storage is unavailable.
Reload keeps a persisted set. Clearing browser data removes it. Local preferences are not automatically imported on sign-in.
Payment sandbox
IndexedDB database pointcast-nouns-money-sandbox-v1, or an explicit memory store.
IndexedDB survives reload, subject to browser storage policy. It is shared by the browser origin, independent of signed-in account. Memory ends with the store.
Failed account reads show an unavailable state; they do not silently substitute device preferences as account-saved. The sandbox has no account authentication. Signing out does not erase local demo receipts, and another person using the same browser can see them. Use public demo labels; do not put secrets, customer data or payment details in a label.
A store holds at most 250 intents and 1,000 mutation keys. There is no silent eviction. Preserve any useful demo records before intentionally clearing the browser’s sandbox data. Exported or edited local files remain untrusted demo data.
Existing PointCast sign-in remains the account boundary. The generic profile-state PUT route rejects this collection slot with collection-endpoint-required; use the dedicated endpoint. The SDK does not create sessions, link wallets, ask for wallet signatures, change credentials or request spending approval.
08 / PROVENANCE & RIGHTS
Keep the source attached.
The source-100 catalog uses authentic numbered Nouns #0–99. Read the source-set provenance for file evidence and exact-source verification. Catalog IDs label collectible designs; they do not imply ownership of the corresponding NFTs. Earlier ten-design art and generated studies retain their own provenance and labels.
Nouns artwork is available under CC0; using it does not imply Nouns DAO endorsement or NFT ownership. See the official Nouns brand and art resources. The exact artwork claims apply to verified flat masters, not to generated scenes or material effects.
This release does not grant a blanket license over the repository or every third-party asset. Check the existing license or provenance of each code component, font, image and source before reuse. Keep author credits and asset-specific terms with copies. Open documentation and a public specification do not change those terms.
09 / TEZOS WORKSHOP · PROPOSED
No collector gas, with a sponsor.
Design proposal only. Today’s collection makes no blockchain operation. No Nouns Money contract, token issuance, wallet challenge, sponsor service, chain fee budget or transfer service is enabled in this release.
A possible first Tezos companion could use an issuer-funded FA2 mint to a verified recipient. FA2 supports multiple token types and defines transfer and operator behavior; a mint entrypoint is contract-specific, not guaranteed by the standard. Tezos FA2 documentation ↗
Establish consent and recipient control. A future wallet challenge would bind an explicit recipient, application domain, nonce and expiry. Verify it on a server before considering issuance. Arbitrary-data signing exists in wallet tooling; the challenge and verifier still need their own reviewed implementation. Octez Connect signing reference ↗
Authorize the sponsor’s operation. A reviewed, contract-specific mint could issue to the recipient from an authorized issuer account. The official SmartPy tutorial shows administrator-controlled minting to recipients; it is a reference, not a Nouns Money contract. Minting reference ↗
Pay and reconcile. The sponsor would pay authorized fees and storage costs, enforce a budget and duplicate-claim rules, and reconcile the operation’s observed result before recording delivery. “No collector gas” means sponsored costs, not a zero-fee chain. Octez operation reference ↗
Keep later actions separate. An issuer-sponsored mint would not automatically sponsor later transfers. Permits or relayed interactions need a compatible contract and their own authorization, replay protection and review. A future adapter must distinguish requested, submitted, confirmed and failed delivery from today’s local demo receipt.
Before activation, a separate owner authorization must cover the contract, signer, fee budget, abuse controls, recovery and real-chain testing. Issuer, backing asset, merchant network and legal setup are undecided. Nothing here promises issuance, allocation, future value, redemption or an airdrop.
Webhooks also remain proposed. A future delivery service would need authenticated events, event IDs, retries and reconciliation; no callback URL can be registered and no webhook is sent today.
10 / AGENTS & X402 · PROPOSED
Specify the job before any future payment.
Proposed service only. The implemented endpoints expose the art catalog and account preferences. There is no Nouns Money agent-service HTTP route or x402 support in this release.
A future agent-service request could bind a short scope, output constraints, one acceptance-test description, and an expiry. Approval of service work is a separate decision from any future payment. A provider would return an artifact reference and digest; verification would record its method and outcome. The user acceptance decision would remain a separate field.
Local receipt adapter · no network
The TypeScript SDK example uses local memory only. Its explicit approval input represents a local workflow step and grants no financial authority. Run the complete exportable example with node docs/nouns-money/agent-service-quickstart.mjs.
import {
approveLocalAgentServiceJob,
createLocalAgentServiceReceipt,
createLocalAgentServiceRequest,
sha256Hex,
} from './packages/nouns-money-sdk/src/index.ts';
const request = createLocalAgentServiceRequest({
scope: 'text-report',
constraints: { maxOutputBytes: 256, mediaType: 'text/plain' },
acceptanceTest: 'Compare the returned bytes to local expected text.',
});
// Separate local job approval. This authorizes no payment.
const approval = approveLocalAgentServiceJob(request, { approved: true });
const expected = new TextEncoder().encode('Local result.\n');
const bytes = new TextEncoder().encode('Local result.\n');
const receipt = await createLocalAgentServiceReceipt({
request, approval, providerId: 'local-demo',
resultRef: 'local://artifact/result.txt', bytes,
expectedSha256: await sha256Hex(expected),
});
console.log(JSON.stringify(receipt, null, 2));
// unsigned/unverified · acceptance pending · payment not requested
The resulting record contains pointcast.nouns-money.agent-service-receipt/v1, an artifact digest, paymentStatus: not_requested, acceptanceStatus: pending and authenticity: unsigned_unverified.
What the digest checks
verification.method is sha-256-content-bytes; the outcome compares the produced bytes with separately supplied expected bytes. A match proves byte consistency only. It does not establish truth, quality, safety, ownership, delivery to another party, or user acceptance.
Never use an unsigned local JSON receipt as proof of provider identity or payment. The request, approval and artifact references in this sample are local examples, not account, merchant, or payment records.
Possible x402 V2 adapter boundary
As checked on 2 October 2026, the official HTTP guide describes the x402 V2 exchange through PAYMENT-REQUIRED (server requirements), PAYMENT-SIGNATURE (client payment payload) and PAYMENT-RESPONSE (server settlement-attempt result). These messages can carry payment requirements and settlement information; they do not define this proposed service-receipt schema or a user-acceptance contract. Read the HTTP 402 guide ↗, client/server flow ↗, and payment-identifier extension ↗.
A future adapter would require its own approved asset, network, scheme, facilitator and settlement implementation. It would keep request approval separate from payment approval and keep service result, payment status, reconciliation and user acceptance as distinct records. Any replay-protection extension must be explicitly advertised and implemented server-side; the SDK’s local idempotency keys do not substitute for x402 payment identifiers.
No Nouns Money x402 route, automatic payer, payment signer, facilitator connection, payment credential, network setting, HTTP intent endpoint or webhook delivery is present. Do not reuse existing PointCast X402_MODE=test as a dry-run switch: it labels receipts but does not prevent the settlement call. OpenAPI includes a descriptive proposed-integration extension only, with no new HTTP paths.