{"ok":true,"version":"agent-listener-experience.v1","generatedAt":"2026-05-30T05:21:28.558Z","surface":{"id":"listener-experience","artistId":"laurel","safeForAgent":true,"browserAutomationRequired":false,"relatedIssues":[448,505,524,560,573,599,602,608,609,673,801,805,807,808,916,971,986,1069,1222,1517,1622]},"principal":{"source":"guest","role":"guest","signedIn":false,"displayName":"Guest listener","email":null,"emailVerified":null,"adminRole":null,"capabilities":{"readPublicFanState":true,"readSignedInAccountState":false,"readProfile":false,"readNotificationPreferences":false,"readPurchaseHistory":false,"readEntitlements":false,"readPlaybackState":false,"readLiveReadiness":true,"manageProfile":false,"manageNotificationPreferences":false,"manageListenAlong":false,"startCheckout":false,"postPublicFanContent":false}},"communityMode":{"version":"fanful-community-mode.v1","contractDoc":"docs/platform/fanful-community-mode-contract.md","relatedIssues":[560,1075,1078],"source":"agent-listener-experience","selectedCommunity":{"id":"laurel","slug":"laurel-harned","name":"Laurel Harned","role":"first-default-community","isDefault":true,"artistSite":"https://fanful.net/","rawDatabaseIdsRedacted":true},"activeMode":"fan","availableModes":[{"id":"fan","status":"available","summary":"Listen, watch, chat, support, buy, and participate as a fan of the selected community.","requiredAuthorization":"Public or signed-in listener context for private fan state."},{"id":"publisher","status":"authorization-required","summary":"Administer creator/community commerce, live rooms, messaging, moderation, and settings.","requiredAuthorization":"Server-confirmed creator/admin authorization for selectedCommunity.id.","blockedUnless":"Do not expose or execute publisher actions until the backend resolves creator/admin authorization for this selected community."}],"modeSwitching":{"oneAccount":true,"requiresSignOut":false,"preservesPlayerSession":true,"preservesListenerIdentity":true,"authorizationBoundary":"Switching into publisher mode never grants authority by itself; each creator/admin action must still pass server-side scope and role checks for the selected community."},"agentInstructions":["Display selectedCommunity.name and activeMode before proposing community-scoped actions.","Offer fan actions by default; offer publisher actions only when publisher mode is available or when asking the human to authorize it.","Do not assume Laurel-only global context when future communities are added; carry selectedCommunity.id through proposals and confirmations."],"redactedFields":["community.databaseId","artist.internalId","admin.grantId","agentBearerToken"]},"artistFanFeatures":{"version":"artist-fan-feature-settings.v1","artistId":"laurel","sourceContract":{"file":"src/lib/fan-navigation.ts","settingsFunction":"getArtistFanFeatureSettings","navigationContract":"fanNavigationContract","navigationBuilder":"buildFanNavigationItems"},"settings":{"listening":true,"liveStreams":true,"communityChat":false,"dates":true,"media":true,"press":true,"support":true,"memberships":true,"ideas":true,"roadmap":true,"lessons":false,"shop":true},"capabilities":[{"id":"listening","enabled":true,"currentSource":"laurelSiteConfig.enabledFeatures.listeningRoom or recordingLibrary","runtimeAvailability":"static-config","disabledReason":null},{"id":"liveStreams","enabled":true,"currentSource":"laurelSiteConfig.enabledFeatures.liveRoom","runtimeAvailability":"static-config","disabledReason":null},{"id":"communityChat","enabled":false,"currentSource":"community_chat_settings.enabled plus at least one active community_chat_channels row","runtimeAvailability":"runtime-d1","disabledReason":"Community chat is disabled in D1 or no active community channel is visible."},{"id":"dates","enabled":true,"currentSource":"laurelSiteConfig.enabledFeatures.performanceDates","runtimeAvailability":"static-config","disabledReason":null},{"id":"media","enabled":true,"currentSource":"laurelSiteConfig.enabledFeatures.mediaGallery","runtimeAvailability":"static-config","disabledReason":null},{"id":"press","enabled":true,"currentSource":"laurelSiteConfig.enabledFeatures.epkBooking","runtimeAvailability":"static-config","disabledReason":null},{"id":"support","enabled":true,"currentSource":"laurelSiteConfig.enabledFeatures.support","runtimeAvailability":"static-config","disabledReason":null},{"id":"memberships","enabled":true,"currentSource":"laurelSiteConfig.enabledFeatures.memberships","runtimeAvailability":"static-config","disabledReason":null},{"id":"ideas","enabled":true,"currentSource":"laurelSiteConfig.enabledFeatures.ideaBoard","runtimeAvailability":"static-config","disabledReason":null},{"id":"roadmap","enabled":true,"currentSource":"laurelSiteConfig.enabledFeatures.roadmap","runtimeAvailability":"static-config","disabledReason":null},{"id":"lessons","enabled":false,"currentSource":"active lesson offerings with future open lesson_availability_windows rows","runtimeAvailability":"runtime-d1","disabledReason":"Lessons are disabled because there is no active offering with future open availability."},{"id":"shop","enabled":true,"currentSource":"feature_flags.shop plus at least one active, in-stock public store_items row","runtimeAvailability":"runtime-d1","disabledReason":null}],"navigation":[{"id":"listen","label":"Listen","href":"/listen","requiredAny":["listening"],"status":"available","enabled":true,"enabledBy":["listening"],"disabledReason":null},{"id":"live","label":"Live","href":"/live","requiredAny":["liveStreams","communityChat"],"status":"available","enabled":true,"enabledBy":["liveStreams"],"disabledReason":null},{"id":"press","label":"Press","href":"/press","requiredAny":["press"],"status":"available","enabled":true,"enabledBy":["press"],"disabledReason":null},{"id":"media","label":"Media","href":"/media","requiredAny":["media"],"status":"available","enabled":true,"enabledBy":["media"],"disabledReason":null},{"id":"dates","label":"Dates","href":"/dates","requiredAny":["dates"],"status":"available","enabled":true,"enabledBy":["dates"],"disabledReason":null},{"id":"support","label":"Support","href":"/support","requiredAny":["support","memberships"],"status":"available","enabled":true,"enabledBy":["support","memberships"],"disabledReason":null},{"id":"ideas","label":"Ideas","href":"/ideas","requiredAny":["ideas"],"status":"available","enabled":true,"enabledBy":["ideas"],"disabledReason":null},{"id":"roadmap","label":"Roadmap","href":"https://fanful.net/roadmap","requiredAny":["roadmap"],"status":"available","enabled":true,"enabledBy":["roadmap"],"disabledReason":null},{"id":"lessons","label":"Lessons","href":"/lessons","requiredAny":["lessons"],"status":"unavailable","enabled":false,"enabledBy":[],"disabledReason":"Unavailable because none of lessons are enabled for this artist."},{"id":"shop","label":"Shop","href":"/shop","requiredAny":["shop"],"status":"available","enabled":true,"enabledBy":["shop"],"disabledReason":null}],"liveTabRule":{"navigationItemId":"live","label":"Live","requiredAny":["liveStreams","communityChat"],"rule":"available-when-any-required-capability-is-enabled"}},"listener":{"viewer":{"mode":"guest","displayName":"Guest listener","email":null,"emailVerified":null,"accountRef":"guest-listener","rawIdentifiersRedacted":true,"signInPrompt":{"title":"Join or sign in","body":"Create a listener account to save progress, unlock tracks, leave timed comments, listen with friends, and sync devices.","endpoint":{"href":"https://fanful.net/account","method":"GET","auth":"signed-in","description":"Open the web account surface for settings and receipts."}}},"artists":[{"id":"laurel","slug":"laurel-harned","name":"Laurel Harned","instrument":"Classical Guitar","bio":"Laurel Harned is a classical guitarist exploring the expressive possibilities of the instrument through timeless repertoire and contemporary voices.","relationship":"site-default","features":["listeningRoom","mediaGallery","performanceDates","newsletter","support","recordingLibrary","liveRoom","ideaBoard","roadmap","memberships","epkBooking"]}],"library":{"source":"d1","totalTracks":6,"availableTrackIds":["satie-gymnopedie-video","prelude","allemande"],"queue":{"version":"listener-queue.v1","source":"d1","order":["satie-gymnopedie-video","prelude","allemande","courante","capricho-arabe","asturias"],"items":[{"queueIndex":0,"trackId":"satie-gymnopedie-video","artistId":"laurel","title":"Gymnopedie No. 1","mediaKind":"video","durationSeconds":20,"visibility":"public","access":"playable","streamContentType":"video/mp4"},{"queueIndex":1,"trackId":"prelude","artistId":"laurel","title":"Romance Anonimo","mediaKind":"audio","durationSeconds":167,"visibility":"public","access":"playable","streamContentType":"audio/ogg"},{"queueIndex":2,"trackId":"allemande","artistId":"laurel","title":"El Noi de la Mare","mediaKind":"audio","durationSeconds":133,"visibility":"public","access":"playable","streamContentType":"audio/mpeg"},{"queueIndex":3,"trackId":"courante","artistId":"laurel","title":"Canon in D","mediaKind":"audio","durationSeconds":201,"visibility":"members","access":"locked","streamContentType":null},{"queueIndex":4,"trackId":"capricho-arabe","artistId":"laurel","title":"Recuerdos de la Alhambra","mediaKind":"audio","durationSeconds":240,"visibility":"members","access":"locked","streamContentType":null},{"queueIndex":5,"trackId":"asturias","artistId":"laurel","title":"Asturias (Leyenda)","mediaKind":"audio","durationSeconds":401,"visibility":"paid","access":"locked","streamContentType":null}]},"tracks":[{"id":"satie-gymnopedie-video","artistId":"laurel","title":"Gymnopedie No. 1","subtitle":"Open-license classical guitar video preview","composer":"Erik Satie","album":"Open Guitar Demos","durationSeconds":20,"mediaKind":"video","visibility":"public","access":"playable","requiredEntitlement":null,"stream":{"available":true,"delivery":"media-route","contentType":"video/mp4","hrefRedacted":true},"artwork":{"available":false,"hrefRedacted":false},"progress":{"positionSeconds":0,"percent":0,"updatedAt":null},"offline":{"status":"planned","reason":"Streaming only for now.","hrefRedacted":false}},{"id":"prelude","artistId":"laurel","title":"Romance Anonimo","subtitle":"Open classical guitar demo recording","composer":"Traditional","album":"Open Guitar Demos","durationSeconds":167,"mediaKind":"audio","visibility":"public","access":"playable","requiredEntitlement":null,"stream":{"available":true,"delivery":"media-route","contentType":"audio/ogg","hrefRedacted":true},"artwork":{"available":false,"hrefRedacted":false},"progress":{"positionSeconds":0,"percent":0,"updatedAt":null},"offline":{"status":"eligible","reason":null,"hrefRedacted":true}},{"id":"allemande","artistId":"laurel","title":"El Noi de la Mare","subtitle":"Open classical guitar demo recording","composer":"Traditional Catalan","album":"Open Guitar Demos","durationSeconds":133,"mediaKind":"audio","visibility":"public","access":"playable","requiredEntitlement":null,"stream":{"available":true,"delivery":"external-preview","contentType":"audio/mpeg","hrefRedacted":true},"artwork":{"available":false,"hrefRedacted":false},"progress":{"positionSeconds":0,"percent":0,"updatedAt":null},"offline":{"status":"planned","reason":"Streaming only for now.","hrefRedacted":false}},{"id":"courante","artistId":"laurel","title":"Canon in D","subtitle":"Pachelbel, arranged for guitar","composer":"Johann Pachelbel","album":"Open Guitar Demos","durationSeconds":201,"mediaKind":"audio","visibility":"members","access":"locked","requiredEntitlement":"member","stream":{"available":false,"delivery":"unavailable","contentType":null,"hrefRedacted":false},"artwork":{"available":false,"hrefRedacted":false},"progress":{"positionSeconds":0,"percent":0,"updatedAt":null},"offline":{"status":"locked","reason":"Unlock this track before saving it offline.","hrefRedacted":false}},{"id":"capricho-arabe","artistId":"laurel","title":"Recuerdos de la Alhambra","subtitle":"Francisco Tarrega","composer":"Francisco Tarrega","album":"Open Guitar Demos","durationSeconds":240,"mediaKind":"audio","visibility":"members","access":"locked","requiredEntitlement":"member","stream":{"available":false,"delivery":"unavailable","contentType":null,"hrefRedacted":false},"artwork":{"available":false,"hrefRedacted":false},"progress":{"positionSeconds":0,"percent":0,"updatedAt":null},"offline":{"status":"locked","reason":"Unlock this track before saving it offline.","hrefRedacted":false}},{"id":"asturias","artistId":"laurel","title":"Asturias (Leyenda)","subtitle":"Isaac Albeniz, arranged for guitar","composer":"Isaac Albeniz","album":"Open Guitar Demos","durationSeconds":401,"mediaKind":"audio","visibility":"paid","access":"locked","requiredEntitlement":"patron","stream":{"available":false,"delivery":"unavailable","contentType":null,"hrefRedacted":false},"artwork":{"available":false,"hrefRedacted":false},"progress":{"positionSeconds":0,"percent":0,"updatedAt":null},"offline":{"status":"locked","reason":"Unlock this track before saving it offline.","hrefRedacted":false}}]},"playback":{"active":null,"devices":{"count":0,"onlineCount":0,"deviceIdentifiersRedacted":true},"sync":{"supported":true,"requiresSignIn":true,"stateEndpoint":{"href":"https://fanful.net/api/playback/state","method":"GET","auth":"signed-in","description":"Read active cross-device playback state."},"progressEndpoint":{"href":"https://fanful.net/api/progress","method":"POST","auth":"optional-session","description":"Save durable listening progress."},"listenAlong":{"createEndpoint":{"href":"https://fanful.net/api/listen-along/sessions","method":"POST","auth":"signed-in","description":"Create an expiring Listen Along invite URL and QR payload."},"joinEndpoint":{"href":"https://fanful.net/api/listen-along/join","method":"POST","auth":"signed-in","description":"Join a Listen Along session by invite token."},"stateEndpoint":{"href":"https://fanful.net/api/listen-along/state","method":"GET","auth":"signed-in","description":"Read active Listen Along playback state; POST the same href to write state.","query":["sessionId"]},"leaveEndpoint":{"href":"https://fanful.net/api/listen-along/leave","method":"POST","auth":"signed-in","description":"Leave a Listen Along session."},"revokeEndpoint":{"href":"https://fanful.net/api/listen-along/sessions","method":"PATCH","auth":"signed-in","description":"Revoke a host-owned Listen Along session."}},"socketEndpoint":{"href":"wss://fanful.net/api/playback/sync","method":"WS","auth":"signed-in","description":"Connect a device to playback handoff and presence.","query":["token","deviceId","deviceName"]},"tokenEndpoint":{"href":"https://fanful.net/api/playback/token","method":"POST","auth":"signed-in","description":"Issue a short-lived WebSocket token."},"tokenResponseRedacted":true}},"entitlements":{"active":false,"tierId":null,"tierRank":0,"memberAccess":false,"patronAccess":false,"offline":{"status":"sign-in-required","candidateTrackIds":["prelude"],"manifestEndpoint":null,"policy":"Native apps may cache only tracks returned by the signed offline manifest. Clients must revalidate entitlements before the manifest expiry and remove files when access is revoked.","downloadUrlsRedacted":true}},"profilePreferences":{"version":"fan-profile-preferences.v1","capability":"fan.profile-preferences.read","operationId":"listener.profile-preferences.read","safeForAgent":true,"requiredScopes":["listener:profile:read","listener:preferences:read"],"auth":{"source":"guest","canRead":false,"emailVerified":null,"missingScopes":["listener:profile:read","listener:preferences:read"],"accountRef":"guest-listener"},"redactedFields":["listenerId","betterAuthUserId","deviceId","deviceName","pushToken","pushTokenHash"],"status":"sign-in-required","summary":"Sign in before an agent can read private profile or notification preference status.","profile":null,"notifications":null,"nextActions":[{"id":"sign-in","label":"Sign in","href":"https://fanful.net/account?auth=sign-in"}]},"entitlementStatus":{"version":"fan-membership-entitlements.v1","capability":"fan.membership-entitlements.read","operationId":"listener.membership-entitlements.read","safeForAgent":true,"requiredScopes":["listener:entitlements:read"],"auth":{"source":"guest","canRead":false,"emailVerified":null,"missingScopes":["listener:entitlements:read"],"accountRef":"guest-listener"},"redactedFields":["membership.id","purchase.id","stripeCustomerId","stripeSubscriptionId","stripeCheckoutSessionId","stripePaymentIntentId","listenerId","betterAuthUserId"],"status":"sign-in-required","summary":"Sign in before an agent can read private membership or purchase status.","membership":null,"entitlements":null,"purchases":null,"gatedExperiences":[],"nextActions":[{"id":"sign-in","label":"Sign in","href":"https://fanful.net/account?auth=sign-in"}]},"purchaseLinkPreview":{"version":"fanful.purchase-link-preview.v1","operationId":"listener.purchase-link.preview","contractDoc":"docs/agent/link-cli-purchase-contract.md","safeForAgent":true,"previewOnly":true,"createsStripeCheckoutSession":false,"grantsEntitlement":false,"status":"sign-in-required","request":{"targetKind":"membership","targetId":"patron","amountCents":null},"auth":{"source":"guest","buyerMode":"signed-out","canPreview":false,"requiredScopes":["listener:checkout:preview"],"missingScopes":[],"checkoutCreateScope":"listener:checkout:start"},"target":{"kind":"membership","id":"patron","label":"Patron","summary":"Patron monthly membership for Laurel Harned"},"checkout":{"mode":"subscription","currency":"usd","priceCents":1200,"cadence":"monthly","expiresInMinutes":30,"stripePriceRef":"server-owned-stripe-price-redacted","price":"$12.00"},"expiration":{"expiresInMinutes":30,"reason":"Previewed purchase terms are intentionally short-lived and must be regenerated before create."},"confirmation":{"copyVersion":"fanful.purchase-confirmation.v1","targetSummary":"Patron monthly membership for Laurel Harned at $12.00","text":"I confirm starting checkout for Patron monthly membership for Laurel Harned at $12.00.","acknowledgedRiskRequired":true,"createRequiresEnvelope":"agent-write-envelope.v1"},"blockers":["Sign in before an agent can preview buyer-specific checkout terms."],"redactedEntitlementOutcome":{"kind":"membership_access_after_payment","summary":"Patron membership access starts only after Stripe confirms payment.","rawIdentifiersRedacted":true},"states":[{"status":"sign-in-required","summary":"A listener must sign in before an agent can preview buyer-specific checkout terms."},{"status":"missing-scope","summary":"Scoped listener grants need listener:checkout:preview before private purchase-link previews are returned.","requiredScopes":["listener:checkout:preview"]},{"status":"unavailable-product-price","summary":"The requested product, tier, room, or price is inactive, unsupported, missing, or not ready for checkout."},{"status":"preview-ready","summary":"The manifest can show price, cadence, expiration, confirmation copy, blockers, and redacted outcome without mutating state."}],"redactedFields":["stripePriceId","stripeCheckoutSessionId","stripeCustomerId","stripeSubscriptionId","stripePaymentIntentId","purchase.id","membership.id","listenerId","betterAuthUserId","grantId","agentBearerToken","checkoutUrl"]},"purchaseLinkStatus":{"version":"fanful.purchase-link-status.v1","operationId":"listener.purchase-link.status","contractDoc":"docs/agent/link-cli-purchase-contract.md","safeForAgent":true,"readOnly":true,"status":"not-requested","request":{"reference":null,"idempotencyKeyProvided":false},"auth":{"source":"guest","canRead":false,"requiredScopes":["listener:checkout:start"],"missingScopes":[]},"intent":null,"blockers":["Provide purchaseStatusReference or purchaseStatusIdempotencyKey to inspect a checkout intent."],"states":[{"status":"not-requested","summary":"Provide purchaseStatusReference or purchaseStatusIdempotencyKey to inspect a checkout intent."},{"status":"sign-in-required","summary":"A listener must sign in before an agent can inspect private checkout intent state."},{"status":"missing-scope","summary":"Scoped listener grants need listener:checkout:start before checkout intent status is returned.","requiredScopes":["listener:checkout:start"]},{"status":"found","summary":"A checkout intent was found for this listener or scoped grant and returned with raw ids redacted."}],"redactedFields":["rawCheckoutIntentId","stripeCheckoutSessionId","stripePaymentIntentId","stripeCustomerId","purchase.id","listenerId","betterAuthUserId","grantId","agentBearerToken","checkoutUrl"]},"liveSupport":{"version":"fanful.live-support-readiness.v1","operationId":"listener.live-support.preview","safeForAgent":true,"previewOnly":true,"relatedIssues":["#928","#967","#968","#971","#979","#986","#918"],"status":"available-support-preview-with-first-party-live-attribution","availability":{"webLiveRoomCta":{"status":"available-when-event-is-live","rule":"The web live room shows one-time support only while event.status is live.","suggestedAmountCents":2500,"suggestedAmount":"$25.00"},"genericSupportCheckout":{"status":"previewable","targetKind":"support","targetId":"donation","previewEndpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"GET","auth":"signed-in-listener","description":"Preview generic support donation checkout terms for the live-room support CTA.","query":["purchaseTargetKind=support","purchaseTargetId=donation","purchaseAmountCents=2500"]}},"liveEventAttribution":{"status":"available-first-party-live-room-checkout","relatedIssue":"#968","summary":"First-party web live-room checkouts include liveEventId, liveEventTitle, and supportContext=live-room when event.status is live. Paid purchase rows feed artist/admin live totals by liveEventId; support-only agent checkout starts can attach the same live event context after confirmation."}},"purchaseLinkPreview":{"version":"fanful.purchase-link-preview.v1","operationId":"listener.purchase-link.preview","contractDoc":"docs/agent/link-cli-purchase-contract.md","safeForAgent":true,"previewOnly":true,"createsStripeCheckoutSession":false,"grantsEntitlement":false,"status":"sign-in-required","request":{"targetKind":"support","targetId":"donation","amountCents":2500},"auth":{"source":"guest","buyerMode":"signed-out","canPreview":false,"requiredScopes":["listener:checkout:preview"],"missingScopes":[],"checkoutCreateScope":"listener:checkout:start"},"target":{"kind":"support","id":"donation","label":"Support Laurel","summary":"Help fund recordings, concerts, and new repertoire."},"checkout":{"mode":"payment","currency":"usd","priceCents":2500,"cadence":"one-time","expiresInMinutes":30,"stripePriceRef":"server-owned-price-mapping-pending","price":"$25.00"},"expiration":{"expiresInMinutes":30,"reason":"Previewed purchase terms are intentionally short-lived and must be regenerated before create."},"confirmation":{"copyVersion":"fanful.purchase-confirmation.v1","targetSummary":"Help fund recordings, concerts, and new repertoire at $25.00","text":"I confirm starting checkout for Help fund recordings, concerts, and new repertoire at $25.00.","acknowledgedRiskRequired":true,"createRequiresEnvelope":"agent-write-envelope.v1"},"blockers":["Sign in before an agent can preview buyer-specific checkout terms."],"redactedEntitlementOutcome":{"kind":"supporter_receipt_after_payment","summary":"Support receipt is recorded only after payment is confirmed.","rawIdentifiersRedacted":true},"states":[{"status":"sign-in-required","summary":"A listener must sign in before an agent can preview buyer-specific checkout terms."},{"status":"missing-scope","summary":"Scoped listener grants need listener:checkout:preview before private purchase-link previews are returned.","requiredScopes":["listener:checkout:preview"]},{"status":"unavailable-product-price","summary":"The requested product, tier, room, or price is inactive, unsupported, missing, or not ready for checkout."},{"status":"preview-ready","summary":"The manifest can show price, cadence, expiration, confirmation copy, blockers, and redacted outcome without mutating state."}],"redactedFields":["stripePriceId","stripeCheckoutSessionId","stripeCustomerId","stripeSubscriptionId","stripePaymentIntentId","purchase.id","membership.id","listenerId","betterAuthUserId","grantId","agentBearerToken","checkoutUrl"]},"checkoutStart":{"status":"support-only-ready","operationId":"listener.checkout.start","relatedIssue":"#986","broaderIssue":"#918","endpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"POST","auth":"confirmed-listener","description":"Start a confirmation-gated support donation checkout after previewing listener.purchaseLinkPreview."},"requiredBeforeExecutable":[],"executableTargetKinds":["support"],"blockedTargetKinds":["lesson","paid-room"],"requiredScopes":["listener:checkout:start"],"requiresEnvelope":"agent-write-envelope.v1","createsStripeCheckoutSession":true,"grantsEntitlementBeforeStripeWebhook":false},"boundaries":["The GET readiness block does not create Stripe Checkout Sessions, return checkout URLs, persist purchase intent rows, or grant entitlements.","The live-support checkout-start scenario is support-donation only and requires a first-party verified listener session or scoped listener grant with listener:checkout:start.","Membership and shop checkout creation use listener.purchaseLinkPreview with targetKind=membership or targetKind=shop plus their matching checkout tools; lesson and paid-room checkout creation remain blocked under #918 until their catalog, price, and policy contracts are executable.","Raw Stripe, purchase, membership, listener, Better Auth, and scoped grant identifiers stay redacted."],"redactedFields":["stripePriceId","stripeCheckoutSessionId","stripeCustomerId","stripeSubscriptionId","stripePaymentIntentId","purchase.id","membership.id","listenerId","betterAuthUserId","grantId","agentBearerToken","checkoutUrl"]},"account":{"purchases":{"total":0,"recent":[]},"membership":null,"notifications":null},"navigation":{"version":"listener-navigation.v1","fanFeatureSettings":{"listening":true,"liveStreams":true,"communityChat":false,"dates":true,"media":true,"press":true,"support":true,"memberships":true,"ideas":true,"roadmap":true,"lessons":false,"shop":true},"accountEntry":{"signedOutPrimaryLabel":"Join","signedOutSecondaryLabel":"Sign in","signedInLabel":"Account","profileLabel":"Profile"},"primary":[{"id":"listen","label":"Listen","recommendedPlacement":"tab","requiresSignIn":false,"nativeSurfaceId":null,"webParityPath":"/listen","webHandoffAllowed":false,"requiredAny":["listening"]},{"id":"live","label":"Live","recommendedPlacement":"tab","requiresSignIn":false,"nativeSurfaceId":"live","webParityPath":"/live","webHandoffAllowed":false,"requiredAny":["liveStreams","communityChat"]},{"id":"dates","label":"Dates","recommendedPlacement":"tab","requiresSignIn":false,"nativeSurfaceId":"dates","webParityPath":"/dates","webHandoffAllowed":false,"requiredAny":["dates"]},{"id":"support","label":"Support","recommendedPlacement":"more","requiresSignIn":false,"nativeSurfaceId":"support","webParityPath":"/support","webHandoffAllowed":false,"requiredAny":["support","memberships"]},{"id":"ideas","label":"Ideas","recommendedPlacement":"profile","requiresSignIn":false,"nativeSurfaceId":null,"webParityPath":"/ideas","webHandoffAllowed":false,"requiredAny":["ideas"]},{"id":"shop","label":"Shop","recommendedPlacement":"more","requiresSignIn":false,"nativeSurfaceId":"shop","webParityPath":"/shop","webHandoffAllowed":false,"requiredAny":["shop"]},{"id":"account","label":"Account","recommendedPlacement":"profile","requiresSignIn":false,"nativeSurfaceId":null,"webParityPath":"/account","webHandoffAllowed":false,"requiredAny":[]}]},"nativeSurfaces":{"version":"mobile-native-surfaces.v1","endpoint":{"href":"https://fanful.net/api/mobile/native-surfaces","method":"GET","auth":"optional-session","description":"Read native-ready Live, Dates, Shop, Lessons, Support, and artist go-live surface data."},"surfaceIds":["live","dates","shop","lessons","support","artist-go-live"],"noWebHandoffSurfaceIds":["live","dates","shop","lessons","artist-go-live"],"stripeCheckoutException":true,"summary":"iOS and Android should render these surfaces natively. Support and commerce may use the shared checkout API, then open only Stripe-hosted checkout when payment is required."},"featureFlags":{"webPlayback":"available","durableProgress":"available","crossDevicePlaybackSync":"available","crossArtistLibrary":"contract-only","offlineDownloads":"contract-only","pushNotifications":"contract-only","artistLiveCompanion":"contract-only","carPlay":"planned","airPlay":"planned","deviceHandoff":"available"}},"linkedSurfaces":[{"id":"live-readiness","status":"ready","manifest":{"href":"https://fanful.net/api/agent/live-readiness","method":"GET","auth":"optional-session","description":"Read listener-safe joinability for live rooms, embedded rooms/webinars, and Listen Along."},"mcpResource":"fanful://agent/live-readiness/manifest","relatedIssues":[448,505,528,560,807]},{"id":"community-chat","status":"ready","manifest":{"href":"https://fanful.net/api/agent/community-chat","method":"GET","auth":"optional-session","description":"Read community channels, visible messages, notification routing, and agent operation descriptors."},"mcpResource":"fanful://agent/community-chat/manifest","relatedIssues":[573,608,609]},{"id":"embedded-rooms","status":"ready","manifest":{"href":"https://fanful.net/api/agent/embedded-rooms","method":"GET","auth":"artist-admin-or-approved-automation","description":"Read embedded room contracts and admin-redacted room/roster state."},"mcpResource":"fanful://agent/embedded-rooms/manifest","relatedIssues":[524,608,609,656]},{"id":"live-controls","status":"ready","manifest":{"href":"https://fanful.net/api/agent/live-controls","method":"GET","auth":"optional-session","description":"Read public live-room state plus admin-only live scheduling and moderation operation descriptors."},"mcpResource":"fanful://agent/live-controls/manifest","relatedIssues":[528,608,609,672,704]},{"id":"creator-commerce","status":"ready","manifest":{"href":"https://fanful.net/api/agent/creator-commerce","method":"GET","auth":"guest","description":"Read public lesson/shop state, service price/policy readiness, and admin-redacted creator commerce operation descriptors."},"mcpResource":"fanful://agent/creator-commerce/manifest","relatedIssues":[608,609,671,799]},{"id":"idea-board","status":"ready","manifest":{"href":"https://fanful.net/api/agent/idea-board","method":"GET","auth":"optional-session","description":"Read visible member ideas, signed-in vote state, admin-redacted review fields, confirmed listener submit/vote writes, and creator review/settings contracts."},"mcpResource":"fanful://agent/idea-board/manifest","relatedIssues":[599,602,608,1702]}],"operations":[{"id":"listener.state.read","toolName":"listener_state_read","status":"ready","enabled":true,"summary":"Read the agent-safe listener/fan manifest, including public library state and signed-in account summaries when a session is present.","endpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"GET","auth":"optional-session","description":"Read the sanitized listener/fan agent manifest.","query":["visitorId"]},"confirmation":{"required":false,"kind":"none","reason":null},"audit":{"required":false,"currentRecord":null,"attribution":"Read-only listener state. Signed-in account state is scoped to the submitted first-party session and redacts raw listener ids, media URLs, and payment identifiers."},"input":{"visitorId":{"type":"string","required":false,"description":"Optional anonymous visitor id used only for guest progress lookup parity with the mobile bootstrap."}},"boundaries":["Does not expose raw Better Auth user ids, listener ids, device ids, media stream URLs, offline download URLs, or purchase ids.","Public guests see discoverable fan state; private account summaries require a signed-in first-party session."]},{"id":"listener.live-readiness.read","toolName":"listener_live_readiness_read","status":"ready","enabled":true,"summary":"Read whether the caller can join Laurel live rooms, embedded webinar rooms, and Listen Along sessions, including blockers and next actions.","endpoint":{"href":"https://fanful.net/api/agent/live-readiness","method":"GET","auth":"optional-session","description":"Read listener-safe live, room, webinar, and Listen Along readiness.","query":["eventId","roomId","includeEnded"]},"confirmation":{"required":false,"kind":"none","reason":null},"audit":{"required":false,"currentRecord":null,"attribution":"Read-only readiness. Signed-in joinability is scoped to the submitted first-party session and redacts raw room, media, invite, and payment handles."},"input":{"eventId":{"type":"string","required":false,"description":"Optional live event id."},"roomId":{"type":"string","required":false,"description":"Optional embedded room id."},"includeEnded":{"type":"boolean","required":false}},"boundaries":["Scheduling, go-live, moderation, and roster writes stay in live-controls and embedded-room admin manifests.","Private draft/cancelled events and invite-only rooms return redacted unauthorized readiness to ordinary callers."]},{"id":"listener.profile-preferences.read","toolName":"listener_profile_preferences_read","status":"ready","enabled":false,"summary":"Read the authenticated fan's profile basics, email confirmation state, notification channels, categories, and community-chat notification routing without exposing raw listener or device identifiers.","endpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"GET","auth":"signed-in-listener","description":"Read listener profilePreferences from the sanitized listener/fan agent manifest."},"confirmation":{"required":false,"kind":"none","reason":null},"audit":{"required":false,"currentRecord":null,"attribution":"Read-only profile and notification preference status. First-party sessions and scoped delegated grants are limited to the authenticated listener and redact raw listener, Better Auth, device, and push-token identifiers."},"input":{"requiredScopes":{"type":"array","values":["listener:profile:read","listener:preferences:read"],"required":false,"description":"Scoped agent grants need listener:profile:read plus listener:preferences:read before private profile and notification preference summaries are returned."}},"boundaries":["Does not change notification settings, email addresses, device registrations, credentials, or public profile state.","Signed-out callers receive a sign-in-required status. Scoped agent callers without both listener profile/preference read scopes receive missing-scope with the specific missing scopes.","Device ids, device names, push tokens, push token hashes, Better Auth user ids, and listener ids stay redacted."]},{"id":"listener.profile.display-name.update","toolName":"listener_profile_display_name_update","status":"ready","enabled":false,"summary":"Update the authenticated listener's display name for future account chrome, chat, community, and comment attribution after exact confirmation.","endpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"POST","auth":"confirmed-listener","description":"Confirmed display-name update wrapper over the shared profile validation rules."},"confirmation":{"required":true,"kind":"account-preference","reason":"Display-name writes need explicit listener confirmation, listener:profile:write for scoped grants, stale-state guard, idempotency, and agent client attribution."},"audit":{"required":true,"currentRecord":"user.name + listener_profile_agent_actions + agent_write_idempotency_keys","attribution":"Confirmed agent display-name writes record the listener, scoped grant/client, previous and next display names, confirmation evidence, audit correlation, and idempotency key."},"input":{"action":{"type":"string","required":true,"value":"confirm_listener_display_name_update"},"displayName":{"type":"string","required":true,"description":"Trimmed, non-empty listener display name using the same validation as /api/account/profile."},"observedDisplayName":{"type":"string","required":true,"description":"The current display name from the latest profile read; stale values are rejected."},"envelope":{"type":"object","required":true,"description":"agent-write-envelope.v1 with target.type=listener_profile, target.summary matching the new visible name, exact confirmation text, idempotency key, reason, and audit correlation id."},"requiredScopes":{"type":"array","values":["listener:profile:write"],"required":false,"description":"Scoped agent grants need listener:profile:write before mutating profile data."}},"boundaries":["Does not change email addresses, passwords, newsletter consent, payment identity, device registrations, or notification preferences.","Existing chat/comment history keeps the display name stored at post time; future listener-originated posts use the updated account display name.","The wrapper rejects raw listener/account ids in model-visible actor and target ids; use signed-in-listener plus the latest observed display name instead."]},{"id":"listener.membership-entitlements.read","toolName":"listener_membership_entitlements_read","status":"ready","enabled":false,"summary":"Read the authenticated fan's membership tier, entitlement flags, purchase summary, and gated experiences without exposing raw payment or listener identifiers.","endpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"GET","auth":"signed-in-listener","description":"Read listener entitlementStatus from the sanitized listener/fan agent manifest."},"confirmation":{"required":false,"kind":"none","reason":null},"audit":{"required":false,"currentRecord":null,"attribution":"Read-only entitlement status. First-party sessions and scoped delegated grants are limited to the authenticated listener and redact raw membership, purchase, Stripe, and listener ids."},"input":{"requiredScopes":{"type":"array","values":["listener:entitlements:read"],"required":false,"description":"Scoped agent grants need listener:entitlements:read before private membership or purchase summaries are returned."}},"boundaries":["Does not start checkout, grant access, revoke access, cancel memberships, or expose Stripe customer, subscription, checkout, payment intent, purchase, or membership ids.","Signed-out callers receive a sign-in-required status. Scoped agent callers without listener:entitlements:read receive a missing-scope status."]},{"id":"listener.notification-preferences.update","toolName":"listener_notification_preferences_update","status":"contract-only","enabled":false,"summary":"Future confirmed-write contract for updating the authenticated listener's email, push, and community-chat notification preferences.","endpoint":{"href":"https://fanful.net/api/account/notification-preferences","method":"PATCH","auth":"signed-in-listener","description":"Update per-category email/push preferences plus community chat mention, digest, join, and mute settings."},"confirmation":{"required":true,"kind":"account-preference","reason":"Preference writes need the shared confirmed-write envelope, idempotency, and agent client attribution before MCP/CLI/ChatGPT clients can execute them."},"audit":{"required":true,"currentRecord":"listener_notification_preferences","attribution":"Future agent preference writes must record the listener, scoped grant/client, requested diff, confirmation evidence, and idempotency key."},"input":{"emailAlerts":{"type":"boolean","required":false},"preferences":{"type":"object","required":false},"communityChat":{"type":"object","required":false}},"boundaries":["Changing email delivery requires a verified email, enforced by the existing account preference route.","Device push tokens are not accepted or echoed through this operation.","Agents can read current preference status through listener.profile-preferences.read, but writes remain disabled until the confirmed write envelope is wired."]},{"id":"listener.listen-along.manage","toolName":"listener_listen_along_manage","status":"ready","enabled":false,"summary":"Create, join, leave, revoke, or read a signed-in listener's Listen Along session through existing endpoints.","endpoint":null,"confirmation":{"required":false,"kind":"none","reason":null},"audit":{"required":false,"currentRecord":"listen_along_sessions","attribution":"Listen Along changes are attributed to the signed-in listener session."},"input":{"action":{"type":"enum","values":["create","join","state","leave","revoke"],"required":true},"inviteToken":{"type":"string","required":false},"sessionId":{"type":"string","required":false}},"boundaries":["Invite tokens and session ids are caller-provided or endpoint-returned values; this manifest does not expose active private invites.","Agents should not join or revoke a shared listening session unless the listener asked for that action."]},{"id":"listener.public-content.post","toolName":"listener_public_content_post","status":"contract-only","enabled":false,"summary":"Post timed comments, reactions, or live-room notes as the authenticated listener after confirmation UX lands; community-message reads and posts now live in the community-chat agent surface.","endpoint":null,"confirmation":{"required":true,"kind":"public-content","reason":"Fan comments, reactions, and live-room notes are externally visible actions."},"audit":{"required":true,"currentRecord":"comments/live_chat_messages","attribution":"Future agent writes need visible listener attribution and client-surface metadata."},"input":{},"boundaries":["Community-chat message reads and confirmed community-message posts are handled by /api/agent/community-chat and its MCP tools; other web/mobile public write endpoints stay disabled until confirmation and attribution are explicit.","Creator-speech automation belongs to the community-chat automation surface, not listener public posting."]},{"id":"listener.purchase-link.preview","toolName":"listener_purchase_link_preview","status":"preview-ready","enabled":false,"summary":"Preview buyer-facing purchase-link terms, confirmation copy, blockers, expiration, and redacted entitlement outcome without creating checkout or granting access.","endpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"GET","auth":"signed-in-listener","description":"Read listener.purchaseLinkPreview from the sanitized listener/fan agent manifest.","query":["purchaseTargetKind","purchaseTargetId"]},"confirmation":{"required":false,"kind":"checkout","reason":"Preview is non-mutating. Creating a checkout later must resubmit the returned confirmation copy through the shared confirmed-write envelope."},"audit":{"required":false,"currentRecord":null,"attribution":"Preview does not persist Fanful checkout intent state or call Stripe; future create must record listener/scoped-grant, agent client, confirmation, idempotency, and audit correlation."},"input":{"purchaseTargetKind":{"type":"enum","values":["membership","support","shop","lesson","paid-room"],"required":false,"description":"Defaults to membership."},"purchaseTargetId":{"type":"string","required":false,"description":"Defaults to patron for membership, donation for support, signed-cd for shop, and zoom-lesson for lesson."},"purchaseAmountCents":{"type":"integer","required":false,"description":"Optional positive amount override for support donation previews. Live-room support currently previews donation at 2500 cents."},"requiredScopes":{"type":"array","values":["listener:checkout:preview"],"required":false,"description":"Scoped listener grants need listener:checkout:preview before private purchase-link previews are returned."}},"boundaries":["Does not create Stripe Checkout Sessions, return checkout URLs, persist purchase intent rows, or grant membership, lesson, shop, room, or supporter entitlements.","Returns only public target labels, buyer mode, price/cadence, expiration, confirmation copy, blockers, and redacted entitlement outcome.","Raw Stripe, purchase, membership, listener, Better Auth, and scoped grant identifiers stay redacted.","Canonical preview-first contract: docs/agent/link-cli-purchase-contract.md."]},{"id":"listener.live-support.preview","toolName":"listener_live_support_preview","status":"preview-ready","enabled":true,"summary":"Read live-room support donation availability, generic support checkout preview terms, first-party live-event attribution status, and the support-only checkout-start contract.","endpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"GET","auth":"optional-session","description":"Read listener.liveSupport from the sanitized listener/fan agent manifest.","query":["purchaseTargetKind=support","purchaseTargetId=donation","purchaseAmountCents=2500"]},"confirmation":{"required":false,"kind":"checkout","reason":"Readiness and purchase-link preview are non-mutating. Starting support checkout requires POSTing the confirmed agent-write envelope."},"audit":{"required":false,"currentRecord":null,"attribution":"Preview does not persist checkout or donation state. The support-only create path records listener/scoped-grant, agent client, idempotency, and audit correlation before Stripe."},"input":{"purchaseTargetKind":{"type":"literal","values":["support"],"required":false},"purchaseTargetId":{"type":"literal","values":["donation"],"required":false},"purchaseAmountCents":{"type":"integer","required":false,"description":"Defaults to the live-room support suggestion of 2500 cents."}},"boundaries":["Reports the generic support preview agents can read plus the first-party web checkout path that attributes paid support to a liveEventId.","Support donation, membership, and signed-CD shop checkout starts are executable here; lesson and paid-room checkout starts remain blocked by #918."]},{"id":"listener.purchase-link.status","toolName":"listener_purchase_link_status","status":"ready","enabled":false,"summary":"Read the redacted status of a previously started support, membership, or shop checkout intent by reference or idempotency key.","endpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"GET","auth":"confirmed-listener","description":"Read listener.purchaseLinkStatus from the sanitized listener/fan agent manifest.","query":["purchaseStatusReference","purchaseStatusIdempotencyKey"]},"confirmation":{"required":false,"kind":"checkout","reason":null},"audit":{"required":false,"currentRecord":"agent_checkout_intents","attribution":"Status reads are scoped to the signed-in listener or same scoped listener grant and return only redacted checkout intent state."},"input":{"purchaseStatusReference":{"type":"string","required":false,"description":"Redacted support-xxxxxxxx, membership-xxxxxxxx, or shop-xxxxxxxx reference returned by listener.checkout.start."},"purchaseStatusIdempotencyKey":{"type":"string","required":false,"description":"Original idempotency key for the checkout start."},"requiredScopes":{"type":"array","values":["listener:checkout:start"],"required":false,"description":"Scoped listener grants need listener:checkout:start before checkout status is returned."}},"boundaries":["Status reads do not create Stripe Checkout Sessions, return checkout URLs, or grant entitlements.","Only support donation, membership, and shop checkout intents are readable here; lesson and paid-room starts remain blocked under #918.","Raw checkout intent ids, Stripe checkout ids, payment intents, customer ids, purchase ids, listener ids, Better Auth ids, grant ids, and bearer tokens stay redacted."]},{"id":"listener.checkout.start","toolName":"listener_checkout_start","status":"ready","enabled":false,"summary":"Start a support donation, membership, or shop checkout through a confirmation-gated agent operation.","endpoint":{"href":"https://fanful.net/api/agent/listener-experience","method":"POST","auth":"confirmed-listener","description":"Create a persisted support, membership, or shop checkout intent, then start Stripe Checkout when configured."},"confirmation":{"required":true,"kind":"checkout","reason":"Purchases, memberships, lesson bookings, and donations are billing-impacting."},"audit":{"required":true,"currentRecord":"agent_checkout_intents + purchases","attribution":"Checkout starts record the signed-in listener or scoped grant, agent client metadata, confirmation evidence, idempotency key, audit correlation, and later purchase session metadata."},"input":{"targetKind":{"type":"enum","values":["support","membership","shop"],"required":true},"targetId":{"type":"string","values":["donation","signed-cd"],"dynamicValues":"Active membership tier ids returned by listener.purchaseLinkPreview and creator membership catalogs.","required":true,"description":"Use donation for support checkout, signed-cd for shop checkout, or an active membership tier id for membership checkout."},"amountCents":{"type":"integer","required":false,"description":"Optional support donation amount in cents, from 100 to 100000. Ignored for membership checkout."},"liveEventId":{"type":"string","required":false,"description":"Optional active live event id for live-room support attribution. Not accepted for membership checkout."},"envelope":{"type":"agent-write-envelope.v1","required":true,"description":"Must include the exact listener.checkout.start confirmation text from listener.purchaseLinkPreview, dryRun=false, action.mode=execute, idempotency key, audit correlation id, and reason."},"requiredScopes":{"type":"array","values":["listener:checkout:start"],"required":false,"description":"Scoped listener grants need listener:checkout:start before checkout can start."}},"boundaries":["Use listener.purchase-link.preview first and submit its exact support donation, membership, or shop confirmation copy.","This operation supports support donations, membership tiers, and the signed-cd shop product. Lesson and paid-room checkout starts remain blocked until #918 and target-specific catalog/price/policy blockers are resolved.","A Fanful checkout intent and idempotency record are persisted before Stripe is called; payment or entitlement effects still wait for Stripe webhook/payment confirmation.","Raw Stripe checkout sessions, payment intents, customer ids, Stripe price ids, purchase ids, membership ids, listener ids, Better Auth ids, and grant ids stay redacted from model-visible responses."]},{"id":"listener.offline-media.read","toolName":"listener_offline_media_read","status":"contract-only","enabled":false,"summary":"Read entitlement-scoped offline download manifests outside model-visible logs after media-token handling is explicit.","endpoint":{"href":"https://fanful.net/api/mobile/offline-manifest","method":"GET","auth":"entitled-listener","description":"Existing mobile endpoint for short-lived offline download URLs; not safe as a broad model-visible agent response.","query":["trackId"]},"confirmation":{"required":true,"kind":"offline-media","reason":"Offline manifests contain short-lived media URLs and should not be dumped into model-visible chat logs."},"audit":{"required":false,"currentRecord":null,"attribution":"Offline reads are governed by existing signed-in entitlement checks."},"input":{"trackId":{"type":"string","required":false}},"boundaries":["This agent manifest only reports offline eligibility and redacts offline hrefs.","A future tool should stream the file to the client runtime or app component without echoing raw download URLs in text."]}],"policy":{"writeConfirmation":"Require explicit listener confirmation before public comments/messages/reactions, checkout starts, cancellations, support actions, or any action that spends money or changes public state.","audit":"Private preference writes use existing account tables. Public fan writes and checkout starts need agent client metadata and durable attribution before becoming executable.","secretHandling":"Do not expose raw listener ids, Better Auth user ids, device ids, push tokens, media stream URLs, offline download URLs, invite tokens, Stripe checkout sessions, payment intents, or purchase ids in model-visible output."},"serverGaps":[{"id":"listener.public-write-confirmation","relatedIssue":673,"status":"partial-community-chat-ready","note":"Community-chat message reads and confirmed posts are executable through the community-chat agent surface. Timed comments, reactions, and live-room notes still need explicit agent confirmation and attribution before they become executable tools."},{"id":"listener.checkout-confirmation","relatedIssue":1517,"status":"support-membership-and-shop-ready","note":"Support donation, membership, and signed-cd shop checkout starts now persist confirmed agent checkout intents, and listener agents can inspect redacted intent status by reference or idempotency key. Lesson and paid-room create paths remain blocked by #918 and target-specific catalog/policy work."},{"id":"listener.live-event-donation-attribution","relatedIssue":968,"status":"ready","note":"First-party web live-room support checkouts pass liveEventId into checkout metadata; paid purchase rows preserve liveEventId/liveEventTitle/supportContext and feed artist/admin live totals."},{"id":"listener.idea-board-agent-contract","relatedIssue":602,"status":"ready-read-and-member-write","note":"Idea-board reads are exposed through /api/agent/idea-board and fanful://agent/idea-board/manifest. Member submit/vote writes now have confirmed wrappers; creator review/settings writes remain contract-only until dedicated wrappers consume before/after diffs and the shared agent write envelope."},{"id":"listener.offline-media-agent-delivery","relatedIssue":673,"status":"planned","note":"Offline manifests contain short-lived download URLs. A future tool should deliver media without echoing raw URLs in chat transcripts."}],"redactedFields":["listenerId","betterAuthUserId","deviceId","deviceName","pushToken","pushTokenHash","stream.href","artwork.href","offline.href","offline.downloadUrl","inviteToken","stripeCheckoutSessionId","stripePriceId","stripeCustomerId","stripeSubscriptionId","stripePaymentIntentId","purchase.id","membership.id","grantId","agentBearerToken","checkoutUrl"],"warnings":[]}