{"ok":true,"version":"agent-embedded-rooms.v1","generatedAt":"2026-05-30T06:51:31.183Z","surface":{"id":"embedded-rooms","artistId":"laurel","safeForAgent":true,"browserAutomationRequired":false,"relatedIssues":[505,523,524,650,654,655,656,1028]},"principal":{"source":"guest","role":"guest","signedIn":false,"displayName":"Guest listener","userId":null,"email":null,"adminRole":null,"capabilities":{"readRoomManifest":false,"readRoomRoster":false,"readRoomCalendar":true,"manageRoomRoster":false,"moderateRoomMessages":false,"createRoomMediaToken":false,"postRoomMessage":false}},"rooms":[],"stats":{"totalRooms":0,"liveRooms":0,"scheduledRooms":0,"waitingRooms":0,"roomsNeedingMedia":0},"roster":{"roomId":null,"participants":[],"redactedFields":["userId","mediaParticipantId","mediaRoomId","mediaPresetName","mediaTokenRefreshedAt"]},"operations":[{"id":"embedded.rooms.read","toolName":"embedded_rooms_read","status":"ready","enabled":false,"summary":"Inspect admin-safe Laurel embedded room summaries, access modes, counts, media setup state, and safe readiness blockers.","endpoint":{"href":"https://docs.fanful.net/api/agent/embedded-rooms","method":"GET","auth":"artist-admin-or-approved-automation","description":"Read the agent-safe embedded-room manifest and room summaries.","query":["roomId"]},"confirmation":{"required":false,"kind":"none","reason":null},"audit":{"required":false,"currentRecord":null,"attribution":"Read-only admin/automation access returns redacted room metadata."},"input":{"roomId":{"type":"string","required":false,"description":"Optional room id whose redacted roster should be included for an authorized admin or automation."}},"boundaries":["Does not expose RealtimeKit participant tokens, raw media config, external fallback URLs, or private invite links.","Guest/listener callers receive the contract and capability map but not private room summaries."]},{"id":"embedded.room.roster.read","toolName":"embedded_room_roster_read","status":"ready","enabled":false,"summary":"Read a redacted roster for one embedded room, including email, display name, role, and status.","endpoint":{"href":"https://docs.fanful.net/api/agent/embedded-rooms","method":"GET","auth":"artist-admin-or-approved-automation","description":"Read the embedded-room manifest with roster included for one room.","query":["roomId"]},"confirmation":{"required":false,"kind":"none","reason":null},"audit":{"required":false,"currentRecord":null,"attribution":"Read-only roster inspection for an authorized admin or automation."},"input":{"roomId":{"type":"string","required":true,"description":"Embedded room id."}},"boundaries":["Roster reads omit media participant identifiers and token refresh metadata.","Broad automated roster exports should stay scoped to one requested room unless Mark approves a wider operational report."]},{"id":"embedded.room.roster.manage","toolName":"fanful_creator_embedded_room_roster_update","status":"ready","enabled":false,"summary":"Execute a confirmed, audited roster, co-host, stage, remove, or restore change for one embedded-room participant.","endpoint":{"href":"https://docs.fanful.net/api/agent/embedded-rooms","method":"POST","auth":"artist-admin-or-approved-automation","description":"Confirmed embedded-room roster write with agent-write-envelope.v1, idempotency, stale-state, and audit attribution."},"confirmation":{"required":true,"kind":"room-access","reason":"Roster changes alter listener access, speaker status, and who can join with presenter media presets."},"audit":{"required":true,"currentRecord":"embedded_room_admin_actions + agent_write_idempotency_keys","attribution":"Records the admin or automation identity, target participant, before/after role/status summaries, agent client, tool name, idempotency key, audit correlation id, and reason."},"input":{"action":{"type":"literal","value":"confirm_embedded_room_roster_change","required":true},"roomId":{"type":"string","required":true},"email":{"type":"string","required":true,"format":"email","maxLength":240},"displayName":{"type":"string","required":true,"minLength":1,"maxLength":160},"role":{"type":"enum","required":true,"values":["host","co_host","attendee","stage_guest"]},"status":{"type":"enum","required":true,"values":["invited","joined","left","removed"]},"observedParticipantUpdatedAt":{"type":"string","required":false,"format":"date-time","description":"Required when updating an existing participant; omit only for a new invite."},"envelope":{"type":"agent-write-envelope.v1","required":true}},"boundaries":["The wrapper rejects duplicate idempotency keys, stale existing participant state, and no-op roster changes.","Agents must confirm before co-host promotion, stage promotion/removal, room removal, or participant restore.","Removed participants are denied even when purchase, membership, or public access would otherwise allow entry.","Responses and audit summaries omit media participant identifiers, token refresh secrets, and private invite links."]},{"id":"embedded.room.messages.post","toolName":"embedded_room_messages_post","status":"ready","enabled":false,"summary":"Post an embedded-room chat message as an authenticated viewer who can enter that room.","endpoint":{"href":"https://docs.fanful.net/api/rooms/:roomId/messages","method":"POST","auth":"room-authorized-listener","description":"Post room chat using the same access checks as the web/native room clients."},"confirmation":{"required":true,"kind":"public-message","reason":"Room chat messages are visible to other authorized participants."},"audit":{"required":true,"currentRecord":"embedded_room_messages","attribution":"Stored with the signed-in user's id and display name."},"input":{"roomId":{"type":"string","required":true},"body":{"type":"string","required":true,"minLength":1,"maxLength":1200}},"boundaries":["The message endpoint rejects callers who cannot enter the room.","Automation tokens cannot post as listeners."]},{"id":"embedded.room.media-token.create","toolName":"embedded_room_media_token_create","status":"ready","enabled":false,"summary":"Mint a short-lived embedded-room media token for the signed-in viewer.","endpoint":{"href":"https://docs.fanful.net/api/rooms/:roomId/media-token","method":"POST","auth":"room-authorized-listener","description":"Create a short-lived RealtimeKit token using shared room access and role checks."},"confirmation":{"required":false,"kind":"media-token","reason":"Client token minting follows the explicit user join action and should not be logged."},"audit":{"required":false,"currentRecord":"embedded_room_participants.media_token_refreshed_at","attribution":"Participant media identity is tied to the signed-in user or invited email."},"input":{"roomId":{"type":"string","required":true}},"boundaries":["The manifest never includes raw RealtimeKit tokens.","Tokens are short-lived bearer credentials and must not be copied into model-visible logs."]},{"id":"embedded.room.calendar-export.read","toolName":"embedded_room_calendar_export_read","status":"ready","enabled":true,"summary":"Download a first-party .ics event for a scheduled embedded room the caller can enter.","endpoint":{"href":"https://docs.fanful.net/api/rooms/:roomId/calendar","method":"GET","auth":"room-authorized-listener","description":"Return a text/calendar event with the first-party room URL and no provider/media secrets."},"confirmation":{"required":false,"kind":"none","reason":null},"audit":{"required":false,"currentRecord":null,"attribution":"Calendar export is read-only and reuses the room viewer access check."},"input":{"roomId":{"type":"string","required":true}},"boundaries":["The route returns non-200 JSON instead of a malformed calendar when the room has no valid start/end time.","The calendar never exposes external fallback URLs, raw media config, provider ids, private invite links, or participant identities."]},{"id":"embedded.room.moderation.manage","toolName":"embedded_room_moderation_manage","status":"ready","enabled":false,"summary":"Hide, flag, restore, pin, or unpin embedded-room chat messages through an audited admin route.","endpoint":{"href":"https://docs.fanful.net/api/admin/embedded-rooms/messages","method":"PATCH","auth":"artist-admin-or-approved-automation","description":"Moderate one embedded-room chat message and record a redacted audit row."},"confirmation":{"required":true,"kind":"room-access","reason":"Room moderation changes what participants can see during a private event."},"audit":{"required":true,"currentRecord":"embedded_room_admin_actions","attribution":"Records the admin or automation identity, target message, redacted before/after message summaries, client, tool name, and reason."},"input":{"id":{"type":"string","required":true,"description":"Embedded-room message id."},"status":{"type":"enum","required":false,"values":["visible","hidden","flagged"]},"pinned":{"type":"boolean","required":false},"reason":{"type":"string","required":false,"maxLength":500}},"boundaries":["Do not moderate room chat by direct database writes.","Message bodies are redacted in audit rows; summaries expose body presence and length only.","Confirm before hiding/restoring private-event chat or pinning a message for all room participants."]},{"id":"embedded.rooms.audit.read","toolName":"embedded_rooms_audit_read","status":"ready","enabled":false,"summary":"Read redacted embedded-room roster and chat moderation audit rows.","endpoint":{"href":"https://docs.fanful.net/api/admin/embedded-rooms/audit","method":"GET","auth":"artist-admin-or-approved-automation","description":"Read redacted embedded-room admin action rows.","query":["roomId","participantId","messageId","action","limit"]},"confirmation":{"required":false,"kind":"none","reason":null},"audit":{"required":false,"currentRecord":"embedded_room_admin_actions","attribution":"Read-only access requires the same admin or automation identity as write routes."},"input":{"roomId":{"type":"string","required":false},"participantId":{"type":"string","required":false},"messageId":{"type":"string","required":false},"action":{"type":"enum","required":false,"values":["room_create","room_update","room_status_update","room_media_update","participant_invite","participant_update","participant_role_update","participant_status_update","participant_remove","participant_restore","stage_promote","stage_demote","cohost_promote","cohost_demote","message_moderate","message_pin","message_unpin"]},"limit":{"type":"number","required":false,"min":1,"max":100}},"boundaries":["Audit rows redact message bodies, raw media identifiers, invite token hashes, private invite links, and token refresh secrets."]}],"policy":{"writeConfirmation":"Require exact agent-write-envelope confirmation, idempotency, audit correlation, reason, and stale-state guards before inviting/removing attendees, promoting co-hosts, or moving people on/off stage.","attribution":"Confirmed roster writes record the admin or automation identity, scoped agent client, target participant, before/after summaries, idempotency key, audit correlation id, and reason. Listener chat/media operations use the signed-in listener identity.","secretHandling":"Do not expose raw RealtimeKit tokens, raw media config, private invite links, external fallback URLs, or Stripe checkout/payment identifiers in model-visible logs."},"serverGaps":[{"id":"embedded.rooms.ios-native-parity","relatedIssue":654,"status":"planned","note":"iOS needs equivalent attendee/host states once the native embedded-room surface is scheduled."},{"id":"embedded.rooms.android-native-parity","relatedIssue":655,"status":"planned","note":"Android needs equivalent attendee/host states once the native embedded-room surface is scheduled."}]}