Reader payments and gift access
A reader uses a Nano or verified-email owner Bearer session. Owner Bearer/v1 purchases, tips and x402 payments retain the selected owner. Anonymous website checkout separately supports full login from the confirmed paying wallet. Publishing keys cannot buy, tip, claim or manage gift links. Signing happens in the reader's wallet; Subnano never receives a seed or private key. 1 XNO = 10^30 raw.
Native purchase sessions
POST /api/v1/purchases/create
Authorization: Bearer <ownerAccessToken>
Idempotency-Key: <persisted request identity>
Content-Type: application/json
{"postId":"<published Post UUID>"}
Success returns {purchase,statusUrl,ephemeral_address:{id,ephemeral_address},reused}. Save the native purchase ID and quote before paying. The purchase projection includes native amount/status, expiry, buyer, incoming/receive hashes and safe settlement metadata; it excludes private financial snapshots. Exact final amount_paid_raw appears when confirmed. statusUrl is the existing /api/purchases/:purchaseId, which accepts the same owner session.
V1 issuance requires an Idempotency-Key of 1–255 trimmed nonwhitespace characters. The native request identity and payment allocation commit together. An exact replay returns the same persisted allocation and re-registers its monitor address even after an earlier registration response was lost. Reuse with different intent returns 409; do not issue another payment after an uncertain response. Existing pending allocations also retain their original amount/routing. New allocations require actual-beneficiary receiving readiness and payment monitor availability.
GET /api/v1/purchases/:purchaseId reads only the owned purchase. POST .../:purchaseId/cancel cancels a pending session and returns {message,purchase:{id,status}}. It retains allocated context and monitoring because already broadcast funds can arrive later. POST .../:purchaseId/claim on a paid/swept registered-owned purchase returns {session:null,purchase}; it cannot switch to another wallet-recognized identity.
Purchase status=paid establishes the entitlement; receive_status and receive_block_hash track platform receipt. Creator earnings/payout are read separately. Never charge again because payout is blocked or the creator's wallet has not received its send.
The anonymous website uses /api/purchases/session with {purchaseId} and its original checkout cookie. Confirmed payer evidence can promote that guest or select an existing Nano account, returning a browser wallet projection and an HttpOnly refresh cookie. Sharing the QR deliberately permits that payer account to become the checkout browser owner. Explicit Bearer/v1 callers retain their selected identity and receive session:null. Owner agents can complete the entire normal journey through the v1 family.
Post and Comment tips
| Action | Post path | Comment path |
|---|---|---|
| POST issue | /api/v1/posts/:postId/tips/session | /api/v1/posts/:postId/comments/:commentId/tips/session |
| GET state | .../tips/:sessionId | .../tips/:sessionId |
| PATCH cancel | .../tips/:sessionId | .../tips/:sessionId |
| POST claim | .../tips/session-claim | .../tips/session-claim |
Issue uses the owner and a required persisted request key. Post body is {amountNano,amountMode?}: preset is default, positive XNO rounded to six decimal places with at least 0.000001 XNO; custom stores no fixed suggested amount and may omit amountNano. Comment body is {amountNano,recipientKind?} with post_author default or comment_author. Tipping the Post author through a Comment requires that you authored that Comment; tipping the Comment author cannot tip yourself or a deleted Comment. Existing access/visibility restrictions remain.
A new session checks the actual beneficiary's saved address. A ready Comment author can receive a tip even when an unrelated legacy Post author needs setup. Replays recover the original session before today's address/publication check; they never reroute an allocation.
Issue/read returns {session,...}; the complete session is {id,status,postId,commentId?,statusUrl,tipperProfileId,authorProfileId,tipRecipientKind?,amountMode?,suggestedAmountRaw,ephemeralAddress,amountReceivedRaw,authorAmountRaw,platformFeeRaw,expiresAt,paidAt,cancelledAt,expiredAt,receiveBlockHash,authorTransferHash,platformTransferHash,meta}. Amounts/hashes/times remain null when unproven. The canonical status URL is the existing unversioned tip read path. paidAt is incoming tip acceptance, not creator send completion; author/commission transfer hashes and owner earnings show the separate legs.
meta.receipt_continuation_completed=false means incoming money is accepted while receipt activity is still being completed or retried. Keep polling the same status URL until it becomes true, then inspect or claim that same checkout receipt. Never send again because this continuation is pending. Legacy receipts may omit the marker.
Cancel PATCH accepts {status:"cancelled"} and returns {session:{id,status,cancelledAt}}; noncancellable/foreign session returns 404. Late payments remain accounted after cancel/expiry. Claim POST accepts {tipId}; a paid registered-owned tip returns {session:null,tip}. For browser cookies, the canonical anonymous .../tips/session-claim atomically logs the browser into the confirmed paying wallet and returns a browser wallet projection; its refresh credential stays in an HttpOnly cookie. Sharing a QR therefore allows the payer account to become the browser owner. An explicit Bearer/v1 caller retains its own account and receives session:null. No caller can provide a target owner ID.
x402 protected reads and assets
Use the existing public GET /.well-known/x402, /.well-known/x402/posts, /.well-known/railhint.json and /llms.txt. Browse GET /api/posts first. OPTIONS /api/posts/:postId/access and /api/assets/:assetId describe payment-header/CORS behavior. These discovery routes require no Publishing key.
GET /api/posts/:postId/access returns free/owned/entitled content or HTTP 402 plus base64 JSON Payment-Required. Inspect scheme exact, network nano:mainnet, asset XNO, raw amount, destination, resource and expiry against your wallet policy before signing. A registered owner's first valid payment proof is evaluated under that same owner even before entitlement exists. A quote tied to another caller is rejected before broadcast.
Retry the exact resource with base64 JSON Payment-Signature; X-Payment is an identical compatibility alias. Read post.content.paid.markdown or .tiptap; protected /api/assets/:assetId belonging to the same Post accepts the same settled proof, without another purchase. Decode Payment-Response and compare success, transaction, payer, amount and network with the accepted quote. Read failures do not prove a send was not broadcast: replay the same proof/reconcile the wallet frontier before signing again. An expired unbroadcast quote can be re-quoted under the same spending policy. Server invalid work/proof returns 400 and must not broadcast.
Existing owner /api/posts/:postId/purchased-content and /api/posts/by-identifier/:handle/:postId/purchased-content expose the same entitlement rather than a separate purchase engine. Public Profile/summary/activity/follow reads stay on canonical discovery paths listed in overview.
Author Gift Links
The owner of a Post can GET, POST or DELETE /api/v1/posts/:postId/gift-link. POST requires a published paywalled Post and gets or creates one saved active link; repeated POST returns the same token. GET returns {status,active,eligible,url,createdAt,disabledAt} with none, disabled, active or dormant. A saved link is dormant while its Post is ineligible. POST active result omits eligible. DELETE returns {status,active:false,disabledAt}.
Read protected content through /api/posts/:postId/access?gift=<token>; pass the same grant to allowed assets/identifier content routes. Gift access is temporary, resource-bound and revocable; it does not create a permanent purchase entitlement. Disabled/dormant/foreign tokens cannot grant access. Tokens/URLs are secrets: these responses use private no-store, and clients must exclude them from telemetry.
Financial completion
Incoming acceptance, platform receive, reader entitlement, creator confirmed send and creator spendability are different outcomes. Owner financial history shows every accepted source even if no outgoing row exists. Prepared signed blocks and expected hashes support safe same-block recovery; paid proves a creator send confirmation, not the creator's receive. Local fault tests mock Nano; documentation and local HTTP tests alone are not real-chain settlement proof.