Superwall Paywall Editor [!WARNING] LEGACY. For agents, the visual editor is the second choice. Any screen an agent builds (a paywall, an onboarding, an update-required screen, a lifecycle prompt, an announcement, an offer, a web flow) belongs in the superwall framework : a React mini-app in the user's repo, reviewed as code, localized from message files, previewed in the studio on every device, and shipped with / . Use the skill. If it isn't installed: The framework is in public beta: for now the CLI has its commands only while is set (an means it is missing; ask the user to set it in their…

\\n'}\"\n HTTP_BODY=\"${response%

Superwall Paywall Editor [!WARNING] LEGACY. For agents, the visual editor is the second choice. Any screen an agent builds (a paywall, an onboarding, an update-required screen, a lifecycle prompt, an announcement, an offer, a web flow) belongs in the superwall framework : a React mini-app in the user's repo, reviewed as code, localized from message files, previewed in the studio on every device, and shipped with / . Use the skill. If it isn't installed: The framework is in public beta: for now the CLI has its commands only while is set (an means it is missing; ask the user to set it in their…

\\n'*}\"\n}\n\n_request_api() {\n local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n local url=\"${BASE_URL}${path}\"\n\n local args=(-sS -X \"$method\" -w '\\n%{http_code}' -H \"Authorization: Bearer ${SUPERWALL_API_KEY}\")\n if [[ -n \"$body\" ]]; then\n args+=(-H \"Content-Type: application/json\" -d \"$body\")\n fi\n\n local response\n response=\"$(curl \"${args[@]}\" \"$url\")\"\n\n HTTP_STATUS=\"${response##*

Superwall Paywall Editor [!WARNING] LEGACY. For agents, the visual editor is the second choice. Any screen an agent builds (a paywall, an onboarding, an update-required screen, a lifecycle prompt, an announcement, an offer, a web flow) belongs in the superwall framework : a React mini-app in the user's repo, reviewed as code, localized from message files, previewed in the studio on every device, and shipped with / . Use the skill. If it isn't installed: The framework is in public beta: for now the CLI has its commands only while is set (an means it is missing; ask the user to set it in their…

\\n'}\"\n HTTP_BODY=\"${response%

Superwall Paywall Editor [!WARNING] LEGACY. For agents, the visual editor is the second choice. Any screen an agent builds (a paywall, an onboarding, an update-required screen, a lifecycle prompt, an announcement, an offer, a web flow) belongs in the superwall framework : a React mini-app in the user's repo, reviewed as code, localized from message files, previewed in the studio on every device, and shipped with / . Use the skill. If it isn't installed: The framework is in public beta: for now the CLI has its commands only while is set (an means it is missing; ask the user to set it in their…

\\n'*}\"\n}\n\n_request_anon() {\n local path=\"$1\" body=\"$2\"\n local url=\"${BASE_URL}${path}\"\n\n local response\n response=\"$(curl -sS -X POST -H \"Content-Type: application/json\" \\\n -d \"$body\" -w '\\n%{http_code}' \"$url\")\"\n\n HTTP_STATUS=\"${response##*

Superwall Paywall Editor [!WARNING] LEGACY. For agents, the visual editor is the second choice. Any screen an agent builds (a paywall, an onboarding, an update-required screen, a lifecycle prompt, an announcement, an offer, a web flow) belongs in the superwall framework : a React mini-app in the user's repo, reviewed as code, localized from message files, previewed in the studio on every device, and shipped with / . Use the skill. If it isn't installed: The framework is in public beta: for now the CLI has its commands only while is set (an means it is missing; ask the user to set it in their…

\\n'}\"\n HTTP_BODY=\"${response%

Superwall Paywall Editor [!WARNING] LEGACY. For agents, the visual editor is the second choice. Any screen an agent builds (a paywall, an onboarding, an update-required screen, a lifecycle prompt, an announcement, an offer, a web flow) belongs in the superwall framework : a React mini-app in the user's repo, reviewed as code, localized from message files, previewed in the studio on every device, and shipped with / . Use the skill. If it isn't installed: The framework is in public beta: for now the CLI has its commands only while is set (an means it is missing; ask the user to set it in their…

\\n'*}\"\n}\n\nfail_on_non_2xx() {\n if [[ \"$HTTP_STATUS\" != 2* ]]; then\n local msg\n msg=\"$(echo \"$HTTP_BODY\" | jq -r '.error // .message // empty' 2>/dev/null || true)\"\n if [[ -z \"$msg\" ]]; then\n msg=\"HTTP $HTTP_STATUS\"\n fi\n\n if [[ \"$msg\" == session_expired* || \"$msg\" == session_not_found* || \"$msg\" == session_terminated* || \"$msg\" == unauthorized* ]]; then\n if [[ -f \"$STATE_FILE\" ]]; then\n rm -f \"$STATE_FILE\"\n rmdir \"$STATE_DIR\" 2>/dev/null || true\n echo \"Error: $msg\" >&2\n echo \"Local state cleared. Run 'sw-editor.sh attach \u003cpairing-code> --agent-name \u003cname>' to reattach.\" >&2\n exit 1\n fi\n fi\n\n echo \"Error: $msg\" >&2\n exit 1\n fi\n}\n\nfail_on_non_2xx_api() {\n if [[ \"$HTTP_STATUS\" != 2* ]]; then\n local msg\n msg=\"$(echo \"$HTTP_BODY\" | jq -r '.error // .message // empty' 2>/dev/null || true)\"\n if [[ -z \"$msg\" ]]; then\n msg=\"HTTP $HTTP_STATUS\"\n fi\n echo \"Error: $msg\" >&2\n exit 1\n fi\n}\n\nVALID_AGENTS=\"superwall claude codex cursor opencode windsurf kilocode chatgpt openwebui other\"\n\nresolve_agent_name() {\n local input\n input=\"$(printf '%s' \"$1\" | tr '[:upper:]' '[:lower:]')\"\n case \"$input\" in\n superwall|\"superwall agent\") echo \"Superwall Agent\" ;;\n claude|\"claude code\") echo \"Claude Code\" ;;\n codex) echo \"Codex\" ;;\n cursor) echo \"Cursor\" ;;\n opencode) echo \"OpenCode\" ;;\n windsurf) echo \"Windsurf\" ;;\n kilocode) echo \"Kilocode\" ;;\n chatgpt|\"chatgpt\") echo \"ChatGPT\" ;;\n openwebui|\"open webui\") echo \"Open WebUI\" ;;\n other) echo \"Other\" ;;\n *) echo \"\" ;;\n esac\n}\n\ncmd_attach() {\n local pairing_code=\"\" agent_name=\"\"\n while [[ $# -gt 0 ]]; do\n case \"$1\" in\n --agent-name)\n agent_name=\"$2\"; shift 2 ;;\n --agent-name=*)\n agent_name=\"${1#*=}\"; shift ;;\n --help|-h)\n usage; exit 0 ;;\n -*)\n echo \"Unknown flag: $1\" >&2; exit 1 ;;\n *)\n if [[ -z \"$pairing_code\" ]]; then pairing_code=\"$1\"\n else echo \"Unexpected arg: $1\" >&2; exit 1\n fi\n shift ;;\n esac\n done\n\n if [[ -z \"$pairing_code\" ]]; then\n echo \"Usage: sw-editor.sh attach \u003cpairing-code> --agent-name \u003cname>\" >&2\n exit 1\n fi\n\n if [[ -z \"$agent_name\" ]]; then\n echo \"Error: --agent-name is required. Valid: ${VALID_AGENTS}\" >&2\n exit 1\n fi\n\n local resolved_agent\n resolved_agent=\"$(resolve_agent_name \"$agent_name\")\"\n if [[ -z \"$resolved_agent\" ]]; then\n echo \"Error: unknown agent '${agent_name}'. Valid: ${VALID_AGENTS}\" >&2\n exit 1\n fi\n\n local transport_session_id\n transport_session_id=\"cli-$(jq -nr 'now | tostring | @base64' 2>/dev/null || date +%s)-$\"\n\n local body\n body=\"$(jq -n \\\n --arg pairingCode \"$pairing_code\" \\\n --arg transportSessionId \"$transport_session_id\" \\\n --arg agentName \"$resolved_agent\" \\\n '{\n pairingCode: $pairingCode,\n controllerTransportSessionId: $transportSessionId,\n agentSessionId: $transportSessionId,\n agentName: $agentName,\n clientName: \"sw-editor-cli\",\n clientVersion: \"1.0.0\"\n }')\"\n\n _request_anon \"/editor-sessions/claim\" \"$body\"\n fail_on_non_2xx\n\n local session_id token\n session_id=\"$(echo \"$HTTP_BODY\" | jq -r '.status.sessionId')\"\n token=\"$(echo \"$HTTP_BODY\" | jq -r '.controllerToken')\"\n\n if [[ -z \"$session_id\" || \"$session_id\" == \"null\" || -z \"$token\" || \"$token\" == \"null\" ]]; then\n echo \"Error: claim response missing session or token\" >&2\n echo \"$HTTP_BODY\" >&2\n exit 1\n fi\n\n local attached_at\n attached_at=\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"\n\n local state_json\n state_json=\"$(jq -n \\\n --arg sessionId \"$session_id\" \\\n --arg controllerToken \"$token\" \\\n --arg baseUrl \"$BASE_URL\" \\\n --arg transportSessionId \"$transport_session_id\" \\\n --arg attachedAt \"$attached_at\" \\\n '{sessionId: $sessionId, controllerToken: $controllerToken, baseUrl: $baseUrl, transportSessionId: $transportSessionId, attachedAt: $attachedAt}')\"\n write_state \"$state_json\"\n\n local tool_count\n tool_count=\"$(echo \"$HTTP_BODY\" | jq '.toolDefinitions | length')\"\n echo \"Attached. Browser exposes $tool_count tools. Run 'sw-editor.sh tools' to list them.\"\n}\n\nwrite_state_from_launch_status() {\n local status_body=\"$1\"\n local session_id token transport_session_id\n session_id=\"$(echo \"$status_body\" | jq -r '.editorSessionId // empty')\"\n token=\"$(echo \"$status_body\" | jq -r '.controllerToken // empty')\"\n transport_session_id=\"$(echo \"$status_body\" | jq -r '.controllerTransportSessionId // empty')\"\n\n if [[ -z \"$session_id\" || -z \"$token\" || -z \"$transport_session_id\" ]]; then\n echo \"Error: launch is ready but response is missing session attachment data\" >&2\n echo \"$status_body\" >&2\n exit 1\n fi\n\n local attached_at\n attached_at=\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"\n\n local state_json\n state_json=\"$(jq -n \\\n --arg sessionId \"$session_id\" \\\n --arg controllerToken \"$token\" \\\n --arg baseUrl \"$BASE_URL\" \\\n --arg transportSessionId \"$transport_session_id\" \\\n --arg attachedAt \"$attached_at\" \\\n '{sessionId: $sessionId, controllerToken: $controllerToken, baseUrl: $baseUrl, transportSessionId: $transportSessionId, attachedAt: $attachedAt}')\"\n write_state \"$state_json\"\n}\n\nopen_url() {\n local url=\"$1\"\n if command -v open >/dev/null 2>&1; then\n open \"$url\" >/dev/null 2>&1 || true\n elif command -v xdg-open >/dev/null 2>&1; then\n xdg-open \"$url\" >/dev/null 2>&1 || true\n else\n echo \"No browser opener found. Open the URL manually.\" >&2\n fi\n}\n\nwait_for_launch() {\n local launch_id=\"$1\" timeout_seconds=\"$2\"\n local deadline=$((SECONDS + timeout_seconds))\n\n while (( SECONDS \u003c deadline )); do\n _request_api GET \"/editor-session-launches/${launch_id}\"\n fail_on_non_2xx_api\n\n local status\n status=\"$(echo \"$HTTP_BODY\" | jq -r '.status // empty')\"\n\n case \"$status\" in\n ready)\n write_state_from_launch_status \"$HTTP_BODY\"\n local tool_count\n tool_count=\"$(echo \"$HTTP_BODY\" | jq -r '.toolCount // 0')\"\n echo \"Attached. Browser exposes $tool_count tools. Run 'sw-editor.sh tools' to list them.\"\n return 0\n ;;\n failed|expired)\n local msg\n msg=\"$(echo \"$HTTP_BODY\" | jq -r '.error // \"launch failed\"')\"\n echo \"Error: $msg\" >&2\n exit 1\n ;;\n esac\n\n sleep 2\n done\n\n echo \"Error: timed out waiting for the editor launch to connect.\" >&2\n echo \"Launch ID: ${launch_id}\" >&2\n exit 1\n}\n\ncmd_expose() {\n local application_id=\"\" paywall_id=\"\" agent_name=\"\"\n local should_open=false should_wait=false timeout_seconds=600\n\n while [[ $# -gt 0 ]]; do\n case \"$1\" in\n --application-id)\n application_id=\"$2\"; shift 2 ;;\n --application-id=*)\n application_id=\"${1#*=}\"; shift ;;\n --paywall-id)\n paywall_id=\"$2\"; shift 2 ;;\n --paywall-id=*)\n paywall_id=\"${1#*=}\"; shift ;;\n --agent-name)\n agent_name=\"$2\"; shift 2 ;;\n --agent-name=*)\n agent_name=\"${1#*=}\"; shift ;;\n --timeout)\n timeout_seconds=\"$2\"; shift 2 ;;\n --timeout=*)\n timeout_seconds=\"${1#*=}\"; shift ;;\n --open)\n should_open=true; shift ;;\n --wait)\n should_wait=true; shift ;;\n --help|-h)\n usage; exit 0 ;;\n -*)\n echo \"Unknown flag: $1\" >&2; exit 1 ;;\n *)\n if [[ -z \"$application_id\" ]]; then application_id=\"$1\"\n elif [[ -z \"$paywall_id\" ]]; then paywall_id=\"$1\"\n else echo \"Unexpected arg: $1\" >&2; exit 1\n fi\n shift ;;\n esac\n done\n\n if [[ -z \"$application_id\" || -z \"$paywall_id\" ]]; then\n echo \"Usage: sw-editor.sh expose --application-id \u003cid> --paywall-id \u003cid> --agent-name \u003cname> [--open] [--wait]\" >&2\n exit 1\n fi\n\n if [[ -z \"$agent_name\" ]]; then\n echo \"Error: --agent-name is required. Valid: ${VALID_AGENTS}\" >&2\n exit 1\n fi\n\n if ! [[ \"$application_id\" =~ ^[0-9]+$ && \"$paywall_id\" =~ ^[0-9]+$ ]]; then\n echo \"Error: application id and paywall id must be numbers\" >&2\n exit 1\n fi\n\n local resolved_agent\n resolved_agent=\"$(resolve_agent_name \"$agent_name\")\"\n if [[ -z \"$resolved_agent\" ]]; then\n echo \"Error: unknown agent '${agent_name}'. Valid: ${VALID_AGENTS}\" >&2\n exit 1\n fi\n\n require_api_key\n\n local transport_session_id\n transport_session_id=\"cli-launch-$(jq -nr 'now | tostring | @base64' 2>/dev/null || date +%s)-$\"\n\n local body\n body=\"$(jq -n \\\n --argjson applicationId \"$application_id\" \\\n --argjson paywallId \"$paywall_id\" \\\n --arg agentName \"$resolved_agent\" \\\n --arg transportSessionId \"$transport_session_id\" \\\n --arg editorBaseUrl \"$EDITOR_WEB_URL\" \\\n '{\n applicationId: $applicationId,\n paywallId: $paywallId,\n agentName: $agentName,\n agentSessionId: $transportSessionId,\n controllerTransportSessionId: $transportSessionId,\n clientName: \"sw-editor-cli\",\n clientVersion: \"1.0.0\",\n editorBaseUrl: $editorBaseUrl\n }')\"\n\n _request_api POST \"/editor-session-launches\" \"$body\"\n fail_on_non_2xx_api\n\n local launch_id launch_url\n launch_url=\"$(echo \"$HTTP_BODY\" | jq -r '.launchUrl')\"\n launch_id=\"${launch_url#*sw_editor_launch=}\"\n launch_id=\"${launch_id%%[&#]*}\"\n\n if [[ -z \"$launch_url\" || \"$launch_url\" == \"null\" || -z \"$launch_id\" || \"$launch_id\" == \"$launch_url\" ]]; then\n echo \"Error: launch response missing launchUrl\" >&2\n echo \"$HTTP_BODY\" >&2\n exit 1\n fi\n\n echo \"Open this editor URL to expose the session:\"\n echo \"$launch_url\"\n\n if [[ \"$should_open\" == true ]]; then\n open_url \"$launch_url\"\n fi\n\n if [[ \"$should_wait\" == true ]]; then\n wait_for_launch \"$launch_id\" \"$timeout_seconds\"\n else\n echo \"After opening it, run: sw-editor.sh wait-expose ${launch_id}\"\n fi\n}\n\ncmd_wait_expose() {\n local launch_id=\"\" timeout_seconds=600\n\n while [[ $# -gt 0 ]]; do\n case \"$1\" in\n --timeout)\n timeout_seconds=\"$2\"; shift 2 ;;\n --timeout=*)\n timeout_seconds=\"${1#*=}\"; shift ;;\n --help|-h)\n usage; exit 0 ;;\n -*)\n echo \"Unknown flag: $1\" >&2; exit 1 ;;\n *)\n if [[ -z \"$launch_id\" ]]; then launch_id=\"$1\"\n else echo \"Unexpected arg: $1\" >&2; exit 1\n fi\n shift ;;\n esac\n done\n\n if [[ -z \"$launch_id\" ]]; then\n echo \"Usage: sw-editor.sh wait-expose \u003claunch-id> [--timeout \u003cseconds>]\" >&2\n exit 1\n fi\n\n require_api_key\n wait_for_launch \"$launch_id\" \"$timeout_seconds\"\n}\n\ncmd_tools() {\n require_state\n local session_id\n session_id=\"$(state_field sessionId)\"\n _request GET \"/editor-sessions/${session_id}/tools\"\n fail_on_non_2xx\n echo \"$HTTP_BODY\" | jq '.'\n}\n\ncmd_call() {\n require_state\n\n local tool_name=\"\" args_json=\"{}\"\n while [[ $# -gt 0 ]]; do\n case \"$1\" in\n --args)\n args_json=\"$2\"; shift 2 ;;\n --args=*)\n args_json=\"${1#*=}\"; shift ;;\n --help|-h)\n usage; exit 0 ;;\n -*)\n echo \"Unknown flag: $1\" >&2; exit 1 ;;\n *)\n if [[ -z \"$tool_name\" ]]; then tool_name=\"$1\"\n else echo \"Unexpected arg: $1\" >&2; exit 1\n fi\n shift ;;\n esac\n done\n\n if [[ -z \"$tool_name\" ]]; then\n echo \"Usage: sw-editor.sh call \u003ctool-name> [--args '\u003cjson>']\" >&2\n exit 1\n fi\n\n if ! echo \"$args_json\" | jq empty >/dev/null 2>&1; then\n echo \"Error: --args must be valid JSON\" >&2\n exit 1\n fi\n\n local body\n body=\"$(jq -n --arg toolName \"$tool_name\" --argjson args \"$args_json\" '{toolName: $toolName, args: $args}')\"\n\n local session_id\n session_id=\"$(state_field sessionId)\"\n _request POST \"/editor-sessions/${session_id}/call-tool\" \"$body\"\n fail_on_non_2xx\n\n echo \"$HTTP_BODY\" | jq '.'\n\n local is_error\n is_error=\"$(echo \"$HTTP_BODY\" | jq -r '.isError // false')\"\n if [[ \"$is_error\" == \"true\" ]]; then\n exit 1\n fi\n}\n\ncmd_status() {\n require_state\n local session_id\n session_id=\"$(state_field sessionId)\"\n _request GET \"/editor-sessions/${session_id}/status\"\n fail_on_non_2xx\n echo \"$HTTP_BODY\" | jq 'del(.sessionId, .controllerTransportSessionId, .controllerInfo.transportSessionId, .controllerInfo.agentSessionId)'\n}\n\ncmd_release() {\n require_state\n local session_id transport_session_id\n session_id=\"$(state_field sessionId)\"\n transport_session_id=\"$(state_field transportSessionId)\"\n local body\n body=\"$(jq -n --arg transportSessionId \"$transport_session_id\" '{controllerTransportSessionId: $transportSessionId}')\"\n _request POST \"/editor-sessions/${session_id}/release\" \"$body\"\n rm -f \"$STATE_FILE\"\n rmdir \"$STATE_DIR\" 2>/dev/null || true\n\n if [[ \"$HTTP_STATUS\" != 2* ]]; then\n echo \"Detached locally. Server reported: HTTP $HTTP_STATUS\" >&2\n else\n echo \"Detached.\"\n fi\n}\n\ncmd_whoami() {\n if [[ ! -f \"$STATE_FILE\" ]]; then\n echo '{\"attached\": false}'\n return\n fi\n jq '{attached: true, baseUrl: .baseUrl, attachedAt: .attachedAt}' \"$STATE_FILE\"\n}\n\nmain() {\n if [[ $# -eq 0 ]]; then\n usage; exit 0\n fi\n\n local cmd=\"$1\"; shift\n case \"$cmd\" in\n attach) cmd_attach \"$@\" ;;\n expose) cmd_expose \"$@\" ;;\n wait-expose) cmd_wait_expose \"$@\" ;;\n tools) cmd_tools \"$@\" ;;\n call) cmd_call \"$@\" ;;\n status) cmd_status \"$@\" ;;\n release) cmd_release \"$@\" ;;\n whoami) cmd_whoami \"$@\" ;;\n --help|-h|help) usage ;;\n *)\n echo \"Unknown command: $cmd\" >&2\n usage\n exit 1 ;;\n esac\n}\n\nmain \"$@\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":18778,"content_sha256":"ca06d77e7163039179917b0592f01e4998a5b9e732f463211d2a871314176c7d"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Superwall Paywall Editor","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"[!WARNING] ","type":"text"},{"text":"LEGACY. For agents, the visual editor is the second choice.","type":"text","marks":[{"type":"strong"}]},{"text":" Any screen an agent builds (a paywall, an onboarding, an update-required screen, a lifecycle prompt, an announcement, an offer, a web flow) belongs in the ","type":"text"},{"text":"superwall framework","type":"text","marks":[{"type":"strong"}]},{"text":": a React mini-app in the user's repo, reviewed as code, localized from message files, previewed in the studio on every device, and shipped with ","type":"text"},{"text":"superwall push","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"promote","type":"text","marks":[{"type":"code_inline"}]},{"text":". Use the ","type":"text"},{"text":"superwall-framework","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill. If it isn't installed:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"npx skills add https://github.com/superwall/skills --skill superwall-framework --global --yes --agent claude-code universal --full-depth","type":"text"}]},{"type":"paragraph","content":[{"text":"The framework is in public beta: for now the CLI has its commands only while ","type":"text"},{"text":"SUPERWALL_CHANNEL=next","type":"text","marks":[{"type":"code_inline"}]},{"text":" is set (an ","type":"text"},{"text":"Unknown command: create","type":"text","marks":[{"type":"code_inline"}]},{"text":" means it is missing; ask the user to set it in their shell rc and relaunch).","type":"text"}]},{"type":"paragraph","content":[{"text":"An editor paywall moves over in one command:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"superwall migrate \u003cpaywall-id>","type":"text"}]},{"type":"paragraph","content":[{"text":"It scaffolds the paywall as code, briefs the rebuild (the framework skill's ","type":"text"},{"text":"references/migrate-from-editor.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the playbook), and shows the original beside the new one in the studio (Compare › Original). The original keeps serving until a campaign is switched to the new one, so nothing changes for users until then.","type":"text"}]},{"type":"paragraph","content":[{"text":"What to do here:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Asked to build any new screen: build it with the framework, not here.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Asked to change an editor paywall: offer the migration first, in one line, saying what it gains (code review, localization files, any component library, the same studio for every screen). If the user says no or wants a quick edit in place, continue with this skill.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never migrate unprompted, and never refuse an explicit editor request.","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Paywalls are built in a browser editor that exposes its tools over an authenticated relay. This skill drives the same surface used by the MCP gateway, so every tool runs inside the live browser session the user has open.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The user wants to build, edit, or review a Superwall paywall, onboarding, or web2app flow.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The user pastes a pairing code and asks you to take over editing.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The user asks \"what tools can you run right now?\" Discover them via the browser, not from memory.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Start here: attach, then discover","type":"text"}]},{"type":"paragraph","content":[{"text":"Never assume a tool name or signature from memory. The browser is the source of truth and its tool set changes across releases.","type":"text"}]},{"type":"paragraph","content":[{"text":"For ","type":"text"},{"text":"--agent-name","type":"text","marks":[{"type":"code_inline"}]},{"text":", use your agent's recognized slug, such as ","type":"text"},{"text":"codex","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"claude","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"cursor","type":"text","marks":[{"type":"code_inline"}]},{"text":". Use ","type":"text"},{"text":"other","type":"text","marks":[{"type":"code_inline"}]},{"text":" for Grok Build, Muse Code, or any agent not listed in the ","type":"text"},{"text":"CLI reference","type":"text","marks":[{"type":"link","attrs":{"href":"references/cli.md#agent-identity","title":null}}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Preferred API launch flow:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Create an auto-expose URL: ","type":"text"},{"text":"scripts/sw-editor.sh expose --application-id \u003cid> --paywall-id \u003cid> --agent-name \u003cagent> --open --wait","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask the user to complete browser authorization if prompted. The editor auto-exposes; do not ask them to click the expose button.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Discover what is available right now: ","type":"text"},{"text":"scripts/sw-editor.sh tools","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Invoke tools: ","type":"text"},{"text":"scripts/sw-editor.sh call \u003ctool-name> --args '\u003cjson>'","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Fallback manual flow:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask the user for the ","type":"text"},{"text":"pairing code","type":"text","marks":[{"type":"strong"}]},{"text":" shown in the editor UI.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Attach: ","type":"text"},{"text":"scripts/sw-editor.sh attach \u003cpairing-code> --agent-name \u003cagent>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Continue with ","type":"text"},{"text":"tools","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"call","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Full CLI reference: ","type":"text"},{"text":"references/cli.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/cli.md","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"How to build and edit","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Workflow, build order, and when to use which tool: ","type":"text"},{"text":"references/workflow.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/workflow.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Native ","type":"text"},{"text":"sw-*","type":"text","marks":[{"type":"code_inline"}]},{"text":" elements (multiple-choice, indicator, drawer, picker, lottie, navigation): ","type":"text"},{"text":"references/native-elements.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/native-elements.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Design standards, review checkpoints, typography, and conversion principles: ","type":"text"},{"text":"references/design.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/design.md","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Orchestration rules","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always establish an attachment before editing. Use ","type":"text"},{"text":"expose --open --wait","type":"text","marks":[{"type":"code_inline"}]},{"text":" when possible, otherwise use ","type":"text"},{"text":"attach \u003cpairing-code> --agent-name \u003cagent>","type":"text","marks":[{"type":"code_inline"}]},{"text":". ","type":"text"},{"text":"tools","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"call","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"status","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"release","type":"text","marks":[{"type":"code_inline"}]},{"text":" all require an attached session.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer ","type":"text"},{"text":"expose --open --wait","type":"text","marks":[{"type":"code_inline"}]},{"text":" when you have ","type":"text"},{"text":"SUPERWALL_API_KEY","type":"text","marks":[{"type":"code_inline"}]},{"text":", an application id, and a paywall id. It uses the same relay as manual pairing but removes the human pairing-code step.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Before calling a tool you have not used this session, run ","type":"text"},{"text":"tools","type":"text","marks":[{"type":"code_inline"}]},{"text":" to confirm it exists and to read the current parameter schema. Tools are defined in the browser bundle, so an updated editor can ship new or renamed tools without changing this skill.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"get_screenshot","type":"text","marks":[{"type":"code_inline"}]},{"text":" (if present in the tool list) every two or three modifications to verify. Don't fly blind.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer semantic tools (","type":"text"},{"text":"update_styles","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"set_text_content","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"set_dynamic_value","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"move_nodes","type":"text","marks":[{"type":"code_inline"}]},{"text":") over re-running ","type":"text"},{"text":"write_html","type":"text","marks":[{"type":"code_inline"}]},{"text":" on existing structure. See ","type":"text"},{"text":"references/workflow.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer native ","type":"text"},{"text":"sw-*","type":"text","marks":[{"type":"code_inline"}]},{"text":" elements over hand-rolled ","type":"text"},{"text":"\u003cdiv>","type":"text","marks":[{"type":"code_inline"}]},{"text":" recreations whenever the UI represents a semantic control. See ","type":"text"},{"text":"references/native-elements.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When parsing CLI output, use ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":", not Python. Example: ","type":"text"},{"text":"sw-editor.sh call get_subtree --args '...' | jq -r '.content[0].text'","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Release when the user is done: ","type":"text"},{"text":"scripts/sw-editor.sh release","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When things go wrong","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"session_not_ready","type":"text","marks":[{"type":"code_inline"}]},{"text":": the browser disconnected or reloaded. Ask the user to bring the editor tab back, then re-attach. The pairing code rotates, so they must provide the new one.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"session_locked","type":"text","marks":[{"type":"code_inline"}]},{"text":": another client is already attached. The user either attached from another MCP client, or a previous CLI attachment wasn't released. They can detach from the editor UI and you can retry.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"unauthorized","type":"text","marks":[{"type":"code_inline"}]},{"text":": the controller token is stale. Re-attach with a fresh pairing code.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"attach_failed: provide a valid current pairingCode","type":"text","marks":[{"type":"code_inline"}]},{"text":": pairing codes expire after ~10 minutes and rotate on detach. Ask the user to show you the current one.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Report what's broken","type":"text"}]},{"type":"paragraph","content":[{"text":"When you confirm an editor tool misbehaves (wrong result, a crash, a schema that doesn't match ","type":"text"},{"text":"tools","type":"text","marks":[{"type":"code_inline"}]},{"text":", a doc here that's wrong), report it yourself: write it with every ID needed to reproduce it, tell the user in one line, send on their yes (or straight away if they've said you may). Strip secrets and customer data.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"superwall feedback \"Called X with Y; expected Z; got W.\" --title \"...\" \\\n --kind bug --command \"sw-editor.sh call \u003ctool>\" --app \u003cid> \\\n --context paywall=\u003cid> --file error.json --json","type":"text"}]}]},"metadata":{"date":"2026-11-02","name":"superwall-editor","author":"@skillopedia","source":{"stars":30,"repo_name":"skills","origin_url":"https://github.com/superwall/skills/blob/HEAD/skills/superwall-editor/SKILL.md","repo_owner":"superwall","body_sha256":"cf8298ba7c83ad48722ca4ec6a28197bccd0c59712fc77502b7902066b930c45","cluster_key":"95ee00fca196cfd359d0ca8c18b693f86258e58382d8b0fba6e3480f17d187af","clean_bundle":{"format":"clean-skill-bundle-v1","source":"superwall/skills/skills/superwall-editor/SKILL.md","text_bytes":38222,"source_commit":"68b955ed80c99b64b6e4286d7f39049325c9c7de","skills_sh_rank":5605,"attachment_count":5,"text_attachments":5,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/superwall-editor/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"integrations-apis","category_label":"Integrations"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"integrations-apis","import_tag":"skills-sh-top20k-2026-11","description":"LEGACY — drives the browser (visual) paywall editor over a pairing code. For any screen an agent builds, use superwall-framework instead (paywalls, onboarding, update-required, lifecycle prompts, announcements, offers and web flows, as React code); to move an existing editor paywall onto it, run `superwall migrate \u003cpaywall-id>`. Use this skill only when the user explicitly wants to edit inside the visual editor, or to read an editor paywall before migrating it. Covers attach/call/release, native sw-* elements and the editor tool surface."}},"renderedAt":1793676289352}

Superwall Paywall Editor [!WARNING] LEGACY. For agents, the visual editor is the second choice. Any screen an agent builds (a paywall, an onboarding, an update-required screen, a lifecycle prompt, an announcement, an offer, a web flow) belongs in the superwall framework : a React mini-app in the user's repo, reviewed as code, localized from message files, previewed in the studio on every device, and shipped with / . Use the skill. If it isn't installed: The framework is in public beta: for now the CLI has its commands only while is set (an means it is missing; ask the user to set it in their…