{"id":305905,"date":"2026-05-11T08:59:01","date_gmt":"2026-05-11T08:59:01","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/flexa-unsubscribe\/"},"modified":"2026-05-20T10:06:14","modified_gmt":"2026-05-20T10:06:14","slug":"flexa-unsubscribe","status":"publish","type":"plugin","link":"https:\/\/ko.wordpress.org\/plugins\/flexa-unsubscribe\/","author":23412324,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"3.1.2","stable_tag":"3.1.2","tested":"6.9.4","requires":"5.8","requires_php":"7.4","requires_plugins":null,"header_name":"Flexa Unsubscribe","header_author":"flexatech","header_description":"Professional email unsubscribe management with HMAC tokens, auto-append logic, and CSV export.","assets_banners_color":"faf8ff","last_updated":"2026-05-20 10:06:14","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":103,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"3.0.2":{"tag":"3.0.2","author":"flexatech","date":"2026-05-11 09:03:22"},"3.1.1":{"tag":"3.1.1","author":"flexatech","date":"2026-05-20 09:21:49"},"3.1.2":{"tag":"3.1.2","author":"flexatech","date":"2026-05-20 10:06:14"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon.svg":{"filename":"icon.svg","revision":3528475,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3528443,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["3.0.2","3.1.1","3.1.2"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3538671,"resolution":"1","location":"assets","locale":"","width":1170,"height":658},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3538671,"resolution":"2","location":"assets","locale":"","width":1170,"height":658},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3538671,"resolution":"3","location":"assets","locale":"","width":1148,"height":658},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3538671,"resolution":"4","location":"assets","locale":"","width":1170,"height":658},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3538671,"resolution":"5","location":"assets","locale":"","width":1170,"height":658},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3538671,"resolution":"6","location":"assets","locale":"","width":1170,"height":658}},"screenshots":{"1":"<strong>Dashboard<\/strong> - stats cards + bar chart of unsubscribes over time + pie chart of top reasons.","2":"<strong>Unsubscribes \/ Blocked emails \/ Re-subscribed<\/strong> - paginated tables with sorting, per-row delete, and CSV export. The Unsubscribes screen also accepts CSV import for bulk-adding opt-out records (skip-on-duplicate).","3":"<strong>Reasons<\/strong> - manage the dropdown options shown on the public unsubscribe form; click-to-edit, \u2191\/\u2193 reorder.","4":"<strong>Settings<\/strong> - enable\/disable auto-append + blocking, tune the exclude-keywords list.","5":"<strong>Appearance<\/strong> - 19 tokens (colors, typography, copy) across three tabs with a live preview panel.","6":"<strong>Blocked Emails<\/strong> - audit log of send attempts that were stopped because the recipient had opted out - it is not the opt-out list itself."}},"plugin_section":[],"plugin_tags":[267,131785,5825,44115,9755],"plugin_category":[41],"plugin_contributors":[255540],"plugin_business_model":[],"class_list":["post-305905","plugin","type-plugin","status-publish","hentry","plugin_tags-email","plugin_tags-gdpr","plugin_tags-mailing-list","plugin_tags-opt-out","plugin_tags-unsubscribe","plugin_category-communication","plugin_contributors-flexatech","plugin_committers-flexatech"],"banners":[],"icons":{"svg":"https:\/\/ps.w.org\/flexa-unsubscribe\/assets\/icon.svg?rev=3528475","icon":"https:\/\/ps.w.org\/flexa-unsubscribe\/assets\/icon.svg?rev=3528475","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/flexa-unsubscribe\/assets\/screenshot-1.png?rev=3538671","caption":"<strong>Dashboard<\/strong> - stats cards + bar chart of unsubscribes over time + pie chart of top reasons."},{"src":"https:\/\/ps.w.org\/flexa-unsubscribe\/assets\/screenshot-2.png?rev=3538671","caption":"<strong>Unsubscribes \/ Blocked emails \/ Re-subscribed<\/strong> - paginated tables with sorting, per-row delete, and CSV export. The Unsubscribes screen also accepts CSV import for bulk-adding opt-out records (skip-on-duplicate)."},{"src":"https:\/\/ps.w.org\/flexa-unsubscribe\/assets\/screenshot-3.png?rev=3538671","caption":"<strong>Reasons<\/strong> - manage the dropdown options shown on the public unsubscribe form; click-to-edit, \u2191\/\u2193 reorder."},{"src":"https:\/\/ps.w.org\/flexa-unsubscribe\/assets\/screenshot-4.png?rev=3538671","caption":"<strong>Settings<\/strong> - enable\/disable auto-append + blocking, tune the exclude-keywords list."},{"src":"https:\/\/ps.w.org\/flexa-unsubscribe\/assets\/screenshot-5.png?rev=3538671","caption":"<strong>Appearance<\/strong> - 19 tokens (colors, typography, copy) across three tabs with a live preview panel."},{"src":"https:\/\/ps.w.org\/flexa-unsubscribe\/assets\/screenshot-6.png?rev=3538671","caption":"<strong>Blocked Emails<\/strong> - audit log of send attempts that were stopped because the recipient had opted out - it is not the opt-out list itself."}],"raw_content":"<!--section=description-->\n<p><strong>Flexa Unsubscribe<\/strong> adds a complete unsubscribe workflow to every email WordPress sends, with a fully-branded admin UI for managing opt-outs, analytics, and the public unsubscribe page.<\/p>\n\n<ul>\n<li><strong>Auto-appends a secure unsubscribe button<\/strong> to outgoing single-recipient emails. Tokens are HMAC-signed using the <code>AUTH_KEY<\/code> in <code>wp-config.php<\/code>, so no database lookup is needed to verify a link.<\/li>\n<li><strong>Blocks outbound mail to unsubscribed addresses<\/strong> before it reaches the mail server. Blocked attempts are logged to a dedicated audit table.<\/li>\n<li><strong>Honours an exclude-keywords list<\/strong> (default: <code>Order, Password, Invoice<\/code>) so transactional mail never gets an unsubscribe link and never gets blocked.<\/li>\n<li><strong>Re-subscribe URL<\/strong> is supported as a first-class action - the plugin can tell opt-outs from opt-backs.<\/li>\n<li><strong>Customisable public page<\/strong> - every color, font, and string on the unsubscribe\/re-subscribe templates is editable from the admin with a live preview.<\/li>\n<\/ul>\n\n<h3>Admin UI<\/h3>\n\n<p>Starting with v3.0.0 the admin is a React single-page application with seven screens:<\/p>\n\n<ul>\n<li><strong>Dashboard<\/strong> - stats cards + bar chart of unsubscribes over time + pie chart of top reasons.<\/li>\n<li><strong>Unsubscribes \/ Blocked emails \/ Re-subscribed<\/strong> - paginated tables with sorting, per-row delete, and CSV export. The Unsubscribes screen also accepts CSV import for bulk-adding opt-out records (skip-on-duplicate).<\/li>\n<li><strong>Reasons<\/strong> - manage the dropdown options shown on the public unsubscribe form; click-to-edit, \u2191\/\u2193 reorder.<\/li>\n<li><strong>Settings<\/strong> - enable\/disable auto-append + blocking, tune the exclude-keywords list.<\/li>\n<li><strong>Appearance<\/strong> - 19 tokens (colors, typography, copy) across three tabs with a live preview panel.<\/li>\n<\/ul>\n\n<p>All screens are powered by a REST API under <code>\/wp-json\/flexa-unsubscribe\/v1\/<\/code>, so external integrations can plug in too.<\/p>\n\n<p><strong>Documentation<\/strong><\/p>\n\n<p>Full user guide, technical reference, and REST API documentation are hosted at:<\/p>\n\n<p>https:\/\/unsubscribe-doc.flexacommerce.com\/<\/p>\n\n<p>A \"Documentation\" link is also added to the plugin row on the <strong>Plugins<\/strong> screen for one-click access from inside WordPress.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin files to <code>\/wp-content\/plugins\/flexa-unsubscribe<\/code>, or install through the WordPress <strong>Plugins<\/strong> screen.<\/li>\n<li>Activate the plugin through the <strong>Plugins<\/strong> screen.<\/li>\n<li>On activation the plugin provisions three database tables: <code>{prefix}flexa_unsubscribes<\/code>, <code>{prefix}flexa_blocked_emails<\/code>, <code>{prefix}flexa_unsubscribe_reasons<\/code> (the last seeded with three default reasons).<\/li>\n<li>Visit <strong>Unsubscribe<\/strong> in the admin sidebar to configure.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"do%20i%20need%20to%20change%20anything%20in%20my%20existing%20email%20sending%20code%3F\"><h3>Do I need to change anything in my existing email sending code?<\/h3><\/dt>\n<dd><p>No. The plugin hooks <code>wp_mail<\/code> with standard WordPress filters. Any plugin or theme that sends mail via <code>wp_mail<\/code> is covered automatically.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20i%20rotate%20%60auth_key%60%3F\"><h3>What happens if I rotate `AUTH_KEY`?<\/h3><\/dt>\n<dd><p>Every in-flight unsubscribe\/resubscribe link becomes invalid, because the HMAC key is <code>AUTH_KEY<\/code>. New links issued after rotation work normally. Existing records in the database are unaffected.<\/p><\/dd>\n<dt id=\"how%20are%20blocked%20emails%20different%20from%20unsubscribed%20addresses%3F\"><h3>How are blocked emails different from unsubscribed addresses?<\/h3><\/dt>\n<dd><p>Unsubscribes is the list of addresses that opted out. <code>Blocked emails<\/code> is the audit log of outgoing sends that were stopped because they targeted an unsubscribed address. One unsubscribe can cause many blocked-email entries over time.<\/p><\/dd>\n<dt id=\"are%20csv%20exports%20safe%20to%20share%20publicly%3F\"><h3>Are CSV exports safe to share publicly?<\/h3><\/dt>\n<dd><p>No - CSV exports contain email addresses. Treat them as PII. The download link is nonce-protected so it's not trivially shareable across sessions.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>3.1.2<\/h4>\n\n<ul>\n<li><strong>Fix:<\/strong> Inline-editing a reason on the <strong>Reasons<\/strong> screen now commits the new value when you click anywhere outside the input. Previously, clicking away discarded the typed value - only pressing Enter would save. Enter still saves; Esc still discards.<\/li>\n<\/ul>\n\n<h4>3.1.1<\/h4>\n\n<ul>\n<li><strong>New:<\/strong> Plugins-list row now shows <strong>Settings<\/strong> and <strong>Documentation<\/strong> links next to <strong>Deactivate<\/strong>, plus a <strong>View documentation<\/strong> link in the row's meta line. The doc site is https:\/\/unsubscribe-doc.flexacommerce.com\/.<\/li>\n<li><strong>i18n:<\/strong> 3 new translatable strings (\"Settings\", \"Documentation\", \"View documentation\").<\/li>\n<\/ul>\n\n<h4>3.1.0<\/h4>\n\n<ul>\n<li><strong>New:<\/strong> CSV import on the Unsubscribes screen. Bulk-add opt-out records by uploading a CSV file - the same <code>Email, Reason, Date<\/code> shape produced by the existing CSV export, so an export from one site can be re-imported on another. Email is the only required column; Reason and Date are optional. A header row is auto-detected; headerless files are also accepted.<\/li>\n<li><strong>Behavior:<\/strong> Existing emails are skipped (the import is idempotent - it never overwrites the original <code>unsubscribed_at<\/code> or reason for a row already on the list). The dialog reports imported \/ skipped \/ failed counts and lists the first 100 row-level errors so you can correct the source file.<\/li>\n<li><strong>Safety limits:<\/strong> 2 MiB max file size, 10,000 rows per import, requires <code>manage_options<\/code> plus the standard REST nonce.<\/li>\n<li><strong>REST:<\/strong> new <code>POST \/flexa-unsubscribe\/v1\/unsubscribes\/import<\/code> endpoint accepts a <code>multipart\/form-data<\/code> upload (field <code>file<\/code>).<\/li>\n<li><strong>i18n:<\/strong> 29 new translatable strings (8 PHP, 21 admin UI), translated across all seven bundled locales.<\/li>\n<\/ul>\n\n<h4>3.0.2<\/h4>\n\n<ul>\n<li><strong>Security:<\/strong> Sanitize <code>$_GET['email']<\/code> and <code>$_GET['token']<\/code> at the read site in the public unsubscribe\/resubscribe handler (<code>sanitize_email<\/code> \/ <code>sanitize_text_field<\/code> + <code>wp_unslash<\/code>), with a documented <code>phpcs:disable WordPress.Security.NonceVerification.Recommended<\/code> since the HMAC token is the CSRF protection layer for these public links.<\/li>\n<li><strong>Compatibility:<\/strong> Replace inline <code>&lt;style&gt;<\/code> and <code>&lt;script&gt;<\/code> blocks in <code>templates\/unsubscribe-page.php<\/code> and <code>templates\/resubscribe-page.php<\/code> with <code>wp_register_style<\/code> \/ <code>wp_enqueue_style<\/code> \/ <code>wp_add_inline_style<\/code> (and the script equivalents), so the public templates pass the WP.org Plugin Check enqueue rule.<\/li>\n<li><strong>Docs:<\/strong> Fix the source-code repository URL in <code>readme.txt<\/code>.<\/li>\n<\/ul>\n\n<h4>3.0.0<\/h4>\n\n<ul>\n<li><strong>Complete admin rewrite.<\/strong> The seven admin pages are now a React single-page app (Vite + TypeScript + shadcn\/ui + Tailwind v4) instead of individual PHP-rendered screens.<\/li>\n<li><strong>New:<\/strong> REST API under <code>\/wp-json\/flexa-unsubscribe\/v1\/<\/code> covering unsubscribes, blocked emails, re-subscribes, reasons, settings, appearance, and analytics. Every admin screen consumes this API.<\/li>\n<li><strong>New:<\/strong> Dashboard with time-series and reasons charts (recharts).<\/li>\n<li><strong>New:<\/strong> Live preview panel on the Appearance screen - see your colors, fonts, and copy applied to a replica of the public unsubscribe page while you edit.<\/li>\n<li><strong>New:<\/strong> Client-side search + server-side sort + server-side pagination on every list screen.<\/li>\n<li><strong>New:<\/strong> URL-synced table state (<code>?page=2&amp;sort=email&amp;order=desc<\/code> bookmarkable) on every list screen.<\/li>\n<li><strong>Security:<\/strong> CSV export <code>admin-post.php<\/code> handlers now verify nonces via <code>check_admin_referer()<\/code>.<\/li>\n<li><strong>Change:<\/strong> Admin menu label is \"Unsubscribe\" (same as pre-2.x) and sits at menu position 60. Slug changed from <code>flexa-su<\/code> to <code>flexa-unsubscribe<\/code> - legacy admin bookmarks will 404.<\/li>\n<li><strong>Change:<\/strong> Removed the <code>flexa_get_analytics_data<\/code> AJAX endpoint, superseded by the REST <code>\/analytics\/*<\/code> routes.<\/li>\n<li><strong>Requires PHP 7.4<\/strong> (was previously unspecified; the plugin now declares the floor).<\/li>\n<\/ul>\n\n<h4>2.0.2<\/h4>\n\n<ul>\n<li>Pagination for large lists.<\/li>\n<\/ul>\n\n<h4>2.0.1<\/h4>\n\n<ul>\n<li>Menu refinements.<\/li>\n<\/ul>\n\n<h4>2.0.0<\/h4>\n\n<ul>\n<li>Analytics page introduced.<\/li>\n<\/ul>","raw_excerpt":"Professional email unsubscribe management with HMAC tokens, auto-appended unsubscribe links, recipient blocking, and CSV import\/export.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/305905","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=305905"}],"author":[{"embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/flexatech"}],"wp:attachment":[{"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=305905"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=305905"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=305905"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=305905"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=305905"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=305905"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}