{"id":323441,"date":"2026-06-10T13:50:50","date_gmt":"2026-06-10T13:50:50","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/htmx-form-engine\/"},"modified":"2026-06-10T13:50:30","modified_gmt":"2026-06-10T13:50:30","slug":"hxfe-code-first-forms","status":"publish","type":"plugin","link":"https:\/\/mlt.wordpress.org\/plugins\/hxfe-code-first-forms\/","author":23511001,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.3.7","stable_tag":"1.3.7","tested":"7.0","requires":"6.0","requires_php":"7.4","requires_plugins":null,"header_name":"HXFE \u2014 Code-First Forms","header_author":"Youhei Okubo","header_description":"Code-first form engine. Define forms as PHP arrays \u2014 contact forms, step forms, chatbots, and surveys from one schema. Git-manageable, deploy-safe, zero cookies.","assets_banners_color":"3d3b4b","last_updated":"2026-06-10 13:50:30","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/wordpress.org\/plugins\/hxfe-code-first-forms\/","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":47,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.3.7":{"tag":"1.3.7","author":"youheiokubo","date":"2026-06-10 13:50:30"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3567567,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3567567,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3567567,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3567567,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.3.7"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[2364,358,2253,229289,266516],"plugin_category":[],"plugin_contributors":[266517],"plugin_business_model":[],"class_list":["post-323441","plugin","type-plugin","status-publish","hentry","plugin_tags-chatbot","plugin_tags-contact-form","plugin_tags-form-builder","plugin_tags-htmx","plugin_tags-step-form","plugin_contributors-youheiokubo","plugin_committers-youheiokubo"],"banners":{"banner":"https:\/\/ps.w.org\/hxfe-code-first-forms\/assets\/banner-772x250.png?rev=3567567","banner_2x":"https:\/\/ps.w.org\/hxfe-code-first-forms\/assets\/banner-1544x500.png?rev=3567567","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/hxfe-code-first-forms\/assets\/icon-128x128.png?rev=3567567","icon_2x":"https:\/\/ps.w.org\/hxfe-code-first-forms\/assets\/icon-256x256.png?rev=3567567","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p><strong>HXFE \u2014 Code-First Forms<\/strong> is a code-first WordPress form plugin. Instead of building forms in a GUI, you define them as PHP arrays and place a shortcode anywhere.<\/p>\n\n<p>This means your forms live in your codebase \u2014 version-controlled with Git, automatically deployed, and free from database migration issues.<\/p>\n\n<h4>Why code-first?<\/h4>\n\n<ul>\n<li><strong>Git history for free<\/strong> \u2014 Every change to your form shows up in <code>git diff<\/code><\/li>\n<li><strong>Deploy without fear<\/strong> \u2014 Forms are code, so they deploy with your theme. No more \"the form disappeared on production\"<\/li>\n<li><strong>Dynamic options<\/strong> \u2014 Pull select options from <code>get_posts()<\/code>, taxonomies, or any PHP source. No manual updates<\/li>\n<li><strong>One schema, four UIs<\/strong> \u2014 Add <code>step_mode: chatbot<\/code> or <code>one_by_one<\/code> to transform the same fields into a completely different interface<\/li>\n<\/ul>\n\n<h4>Four UI modes from one schema<\/h4>\n\n<ul>\n<li><strong>Normal form<\/strong> \u2014 Classic input \u2192 confirm \u2192 complete flow (default)<\/li>\n<li><strong>Step form<\/strong> \u2014 Multi-page with progress bar (<code>steps<\/code> array)<\/li>\n<li><strong>One-by-one<\/strong> \u2014 Question-at-a-time survey style (<code>step_mode: 'one_by_one'<\/code>)<\/li>\n<li><strong>Chatbot<\/strong> \u2014 Chat bubble interface with typing animation, header, and timestamps (<code>step_mode: 'chatbot'<\/code>)<\/li>\n<\/ul>\n\n<h4>Key features<\/h4>\n\n<ul>\n<li>15 field types: text, email, tel, url, textarea, select, radio, checkbox, checkbox_group, number, date, file, honeypot, reCAPTCHA, privacy<\/li>\n<li>Conditional logic: show_if, required_if, skip_if (hide_if deprecated) \u2014 works in chatbot mode too<\/li>\n<li>Dynamic routing: to_rules, subject_rules, complete_redirect_rules, complete_html_rules based on submitted values<\/li>\n<li>Diagnosis mode: use <code>complete_html_rules<\/code> without <code>to<\/code> \u2014 show results without sending email<\/li>\n<li>Download after submit: <code>download_url<\/code> shows a download button on the complete screen (document request forms)<\/li>\n<li>Form availability window: <code>available_from<\/code> \/ <code>available_until<\/code> with custom before\/after HTML<\/li>\n<li>Custom validation: <code>pattern<\/code>, <code>minlength<\/code>, <code>maxlength<\/code>, <code>error_message<\/code> schema keys + <code>hxfe_validate_field<\/code> and <code>hxfe_validate_form<\/code> filter hooks<\/li>\n<li>Field HTML injection: <code>before_html<\/code> \/ <code>after_html<\/code> schema keys<\/li>\n<li>Page slug tracking: subject auto-appended with <code>[form-id@page-slug]<\/code> for per-page aggregation<\/li>\n<li>Webhook support: send to Zapier, Make, Slack, or any HTTP endpoint<\/li>\n<li>SMTP built-in: Gmail, SendGrid, Mailgun, or custom SMTP<\/li>\n<li>File upload: attached to email, auto-deleted after send<\/li>\n<li>IP restriction and password-protected forms<\/li>\n<li>iframe embedding with per-form CORS control (<code>allowed_origins<\/code>)<\/li>\n<li>Zero cookies: GDPR\/EU cookie-compliant by design<\/li>\n<li>Clean default styles: CSS custom properties (design tokens) for easy theme integration, responsive at 768px<\/li>\n<li>Schema examples panel in admin: 11 copy-paste samples to get started fast<\/li>\n<li>AI-friendly: ships with <code>llms.txt<\/code> and <code>ai-reference.md<\/code> for agentic coding tools<\/li>\n<\/ul>\n\n<h4>Minimum example<\/h4>\n\n<pre><code>add_filter( 'hxfe_schemas', function( $schemas ) {\n    $schemas['contact'] = [\n        'id'      =&gt; 'contact',\n        'to'      =&gt; 'admin@example.com',\n        'subject' =&gt; 'Contact: {name}',\n        'fields'  =&gt; [\n            [ 'key' =&gt; 'name',  'type' =&gt; 'text',     'label' =&gt; 'Name',    'required' =&gt; true ],\n            [ 'key' =&gt; 'email', 'type' =&gt; 'email',    'label' =&gt; 'Email',   'required' =&gt; true ],\n            [ 'key' =&gt; 'body',  'type' =&gt; 'textarea', 'label' =&gt; 'Message', 'required' =&gt; true ],\n            [ 'key' =&gt; 'hp',    'type' =&gt; 'honeypot' ],\n        ],\n    ];\n    return $schemas;\n} );\n<\/code><\/pre>\n\n<p>Shortcode: <code>[hxfe_form id=\"contact\"]<\/code><\/p>\n\n<h4>Chatbot example<\/h4>\n\n<pre><code>$schemas['support'] = [\n    'id'        =&gt; 'support',\n    'to'        =&gt; 'admin@example.com',\n    'step_mode' =&gt; 'chatbot',\n    'bot_name'  =&gt; 'Support Bot',\n    'bot_icon'  =&gt; '\ud83e\udd16',\n    'greeting'  =&gt; 'Hi! How can I help you today?',\n    'fields'    =&gt; [\n        [ 'key' =&gt; 'name',  'type' =&gt; 'text',  'label' =&gt; 'Name',\n          'bot_message' =&gt; 'What is your name?' ],\n        [ 'key' =&gt; 'email', 'type' =&gt; 'email', 'label' =&gt; 'Email',\n          'bot_message' =&gt; 'Thanks {name}! What is your email?' ],\n        [ 'key' =&gt; 'hp', 'type' =&gt; 'honeypot' ],\n    ],\n];\n<\/code><\/pre>\n\n<h4>Diagnosis chatbot (no email)<\/h4>\n\n<pre><code>$schemas['diagnosis'] = [\n    'id'        =&gt; 'diagnosis',\n    \/\/ No 'to' \u2014 result shown without sending email\n    'step_mode' =&gt; 'chatbot',\n    'complete_html_rules' =&gt; [\n        [ 'when' =&gt; ['plan', '==', 'basic'],\n          'html' =&gt; '&lt;h3&gt;Basic plan recommended&lt;\/h3&gt;&lt;p&gt;Hi {name}!&lt;\/p&gt;' ],\n        [ 'when' =&gt; 'default',\n          'html' =&gt; '&lt;p&gt;Thank you, {name}. We will be in touch.&lt;\/p&gt;' ],\n    ],\n    'fields' =&gt; [ ... ],\n];\n<\/code><\/pre>\n\n<h4>Organize schemas in separate files<\/h4>\n\n<pre><code>\/\/ functions.php \u2014 one line\nrequire_once get_template_directory() . '\/inc\/hxfe-forms.php';\n<\/code><\/pre>\n\n<p>Or use HXFE as a standalone plugin with <code>glob()<\/code> auto-loading.<\/p>\n\n<h3>External Services<\/h3>\n\n<p>This plugin optionally connects to Google reCAPTCHA when the <code>recaptcha<\/code> field type is enabled in a form schema.<\/p>\n\n<p><strong>What the service is and what it is used for:<\/strong>\nGoogle reCAPTCHA is a spam-prevention service. When enabled, it loads a script from Google's servers and verifies the user's response server-side to determine whether the form submission is from a human or a bot.<\/p>\n\n<p><strong>What data is sent and when:<\/strong>\nWhen a page containing an HXFE form with reCAPTCHA is loaded, the visitor's browser loads the reCAPTCHA script from <code>google.com<\/code>. On form submission, the reCAPTCHA token generated in the visitor's browser is sent to Google's verification endpoint (<code>https:\/\/www.google.com\/recaptcha\/api\/siteverify<\/code>) along with your site key. No other form field data is transmitted to Google.<\/p>\n\n<p>reCAPTCHA is <strong>disabled by default<\/strong>. It is only active when a site administrator adds a <code>recaptcha<\/code> field to a form schema and configures valid API keys in the plugin settings.<\/p>\n\n<p><strong>Links:<\/strong>\n* Google reCAPTCHA Terms of Service: https:\/\/policies.google.com\/terms\n* Google Privacy Policy: https:\/\/policies.google.com\/privacy<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin folder to <code>\/wp-content\/plugins\/<\/code><\/li>\n<li>Activate in <strong>Plugins \u2192 Installed Plugins<\/strong><\/li>\n<li>Add schemas via the <code>hxfe_schemas<\/code> filter in <code>functions.php<\/code><\/li>\n<li>Place <code>[hxfe_form id=\"your-id\"]<\/code> in any page or post<\/li>\n<li>View all registered forms at <strong>Settings \u2192 Form Engine \u2014 Forms<\/strong><\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20hxfe%20save%20submissions%20to%20the%20database%3F\"><h3>Does HXFE save submissions to the database?<\/h3><\/dt>\n<dd><p>No. HXFE sends email only. This is intentional \u2014 forms defined in code stay lightweight and free of database dependencies. Use Webhook support to send data to external services like Google Sheets or a CRM.<\/p><\/dd>\n<dt id=\"can%20i%20use%20hxfe%20without%20writing%20php%3F\"><h3>Can I use HXFE without writing PHP?<\/h3><\/dt>\n<dd><p>HXFE is designed for developers who want code-first form management. If you need a GUI builder, plugins like WPForms or Fluent Forms may be a better fit.<\/p><\/dd>\n<dt id=\"does%20the%20chatbot%20mode%20work%20with%20conditional%20logic%3F\"><h3>Does the chatbot mode work with conditional logic?<\/h3><\/dt>\n<dd><p>Yes. <code>show_if<\/code> conditions work in chatbot mode \u2014 hidden fields are automatically skipped. You can also use <code>{field_key}<\/code> placeholders in <code>bot_message<\/code> to reference previous answers.<\/p><\/dd>\n<dt id=\"is%20hxfe%20gdpr%20%2F%20eu%20cookie%20compliant%3F\"><h3>Is HXFE GDPR \/ EU cookie compliant?<\/h3><\/dt>\n<dd><p>Yes. HXFE uses zero cookies. Form state is preserved via hidden JSON fields on the server side, not browser storage.<\/p><\/dd>\n<dt id=\"can%20i%20embed%20forms%20on%20external%20domains%3F\"><h3>Can I embed forms on external domains?<\/h3><\/dt>\n<dd><p>Yes. Use <code>[hxfe_iframe id=\"contact\" site=\"https:\/\/your-site.com\"]<\/code> and configure allowed origins in <strong>Settings \u2192 Form Engine \u2192 iframe \/ CORS Settings<\/strong>.<\/p><\/dd>\n<dt id=\"how%20do%20i%20connect%20to%20zapier%20or%20make%3F\"><h3>How do I connect to Zapier or Make?<\/h3><\/dt>\n<dd><p>Add a <code>webhooks<\/code> array to your schema with the target URL. HXFE will POST form data as JSON after each successful submission. Webhook failures do not block form submission.<\/p><\/dd>\n<dt id=\"can%20i%20skip%20the%20confirmation%20screen%3F\"><h3>Can I skip the confirmation screen?<\/h3><\/dt>\n<dd><p>Yes. Add <code>'confirm' =&gt; false<\/code> to your schema. Works for normal forms and step forms.<\/p><\/dd>\n<dt id=\"can%20i%20restrict%20a%20form%20to%20specific%20ip%20addresses%3F\"><h3>Can I restrict a form to specific IP addresses?<\/h3><\/dt>\n<dd><p>Yes. Add <code>allowed_ips<\/code> to your schema with a list of IPs or CIDR ranges. Visitors outside the whitelist see a blocked message, which you can customize with <code>ip_blocked_html<\/code>.<\/p>\n\n<pre><code>allowed_ips =&gt; [ '192.168.1.0\/24', '203.0.113.5' ],\nip_blocked_html =&gt; '&lt;p&gt;This form is only available on the campus network.&lt;\/p&gt;',\n<\/code><\/pre><\/dd>\n<dt id=\"can%20i%20require%20a%20password%20to%20access%20a%20form%3F\"><h3>Can I require a password to access a form?<\/h3><\/dt>\n<dd><p>Yes. Add an <code>auth<\/code> key to your schema. To keep passwords out of Git, define them as constants in <code>wp-config.php<\/code> and reference them in the schema.<\/p>\n\n<p>In <code>wp-config.php<\/code>: <code>define( 'HXFE_STAFF_PASS', 'your-secret-password' );<\/code><\/p>\n\n<p>In your schema:\n    'auth' =&gt; [ 'users' =&gt; [ [ 'id' =&gt; 'staff', 'password' =&gt; defined('HXFE_STAFF_PASS') ? HXFE_STAFF_PASS : '' ] ] ]<\/p>\n\n<p>Brute-force protection is built in \u2014 access is locked for 15 minutes after 5 failed attempts.<\/p><\/dd>\n<dt id=\"can%20i%20prevent%20php%20files%20from%20being%20edited%20via%20the%20wordpress%20admin%3F\"><h3>Can I prevent PHP files from being edited via the WordPress admin?<\/h3><\/dt>\n<dd><p>Yes. Add this to <code>wp-config.php<\/code>:<\/p>\n\n<pre><code>define( 'DISALLOW_FILE_EDIT', true );\n<\/code><\/pre>\n\n<p>This disables the theme and plugin editors in the WordPress admin. It pairs well with HXFE's code-first approach \u2014 forms and code are managed via deployment, not the admin UI.<\/p><\/dd>\n<dt id=\"can%20i%20save%20uploaded%20files%20somewhere%20other%20than%20email%20attachments%3F\"><h3>Can I save uploaded files somewhere other than email attachments?<\/h3><\/dt>\n<dd><p>HXFE deletes uploaded files from the server immediately after sending the email. This is intentional \u2014 keeping files on the server increases security risk and GDPR responsibility.<\/p>\n\n<p>For permanent storage, the recommended approach is to use Gmail + Google Apps Script (GAS): set up a time-based trigger that reads incoming form emails and saves attachments to a designated Google Drive folder. This keeps files off your WordPress server entirely and lets you manage access through Google's permission system.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.3.7<\/h4>\n\n<ul>\n<li>Security: Removed skip_sanitize parameter from hxfe_process_fields() \u2014 all values are now sanitized on every call, including confirm\u2192submit flow<\/li>\n<li>Security: Clarified that sanitize_text_field() and related functions are idempotent, so re-sanitizing already-sanitized values is safe<\/li>\n<\/ul>\n\n<h4>1.3.6<\/h4>\n\n<ul>\n<li>Security: Added nonce verification to hxfe_handle_back() to prevent unauthorized form re-rendering<\/li>\n<li>Security: Back button now passes hxfe_nonce (hxfe_validate_{form_id}) via hx-vals<\/li>\n<li>Code: Clarified skip_sanitize=true comment in hxfe_handle_submit() to explain double-sanitize prevention<\/li>\n<\/ul>\n\n<h4>1.3.5<\/h4>\n\n<ul>\n<li>Renamed schema key: <code>redirect_rules<\/code> \u2192 <code>complete_redirect_rules<\/code> (consistent with other <code>complete_*<\/code> keys)<\/li>\n<li>Renamed schema keys: <code>before_open_html<\/code> \/ <code>after_close_html<\/code> \u2192 <code>before_html<\/code> \/ <code>after_html<\/code><\/li>\n<li>Renamed field keys: <code>min_check<\/code> \/ <code>max_check<\/code> \u2192 <code>min<\/code> \/ <code>max<\/code> (consistent with <code>min_date<\/code> \/ <code>max_date<\/code>)<\/li>\n<li>Deprecated field key <code>hide_if<\/code> \u2014 use <code>show_if<\/code> instead (backward compatible, will be removed in a future version)<\/li>\n<\/ul>\n\n<h4>1.3.4<\/h4>\n\n<ul>\n<li>Added: Page slug auto-injected into subject as [form-id@slug] for per-page tracking<\/li>\n<li>Added: Schema examples panel in admin UI with 7 copy-paste samples<\/li>\n<li>Added: Responsive breakpoint at 768px<\/li>\n<li>Added: CSS custom properties (design tokens) for easy theme customization<\/li>\n<li>Added: <code>download_url<\/code> \/ <code>download_label<\/code> schema keys \u2014 download button on complete screen<\/li>\n<li>Added: <code>available_from<\/code> \/ <code>available_until<\/code> schema keys \u2014 form availability window<\/li>\n<li>Added: <code>before_html<\/code> \/ <code>after_html<\/code> schema keys \u2014 custom messages outside window<\/li>\n<li>Added: <code>allowed_origins<\/code> schema key \u2014 per-form iframe CORS restriction<\/li>\n<li>Added: Embed HTML <code>&lt;iframe&gt;<\/code> copy button in admin form list<\/li>\n<li>Improved: Admin UI redesigned \u2014 stat cards, clean table, monospace field type chips<\/li>\n<li>Improved: chatbot send button replaced with paper-plane SVG icon<\/li>\n<li>Improved: Login\/auth screen with border, padding, full-width button<\/li>\n<li>Improved: iframe \/ CORS Settings tab removed \u2014 now schema-level only<\/li>\n<li>Fixed: hx-encoding=\"multipart\/form-data\" auto-applied when file field present<\/li>\n<li>Fixed: File name shown correctly on confirm screen<\/li>\n<li>Fixed: File attachment preserved through confirm \u2192 submit flow<\/li>\n<li>Fixed: Temporary file cleanup cron (hourly, 1h expiry)<\/li>\n<li>Fixed: Fade-in flash bug (remove+reflow caused visible flicker)<\/li>\n<li>Added: <code>complete_html_rules<\/code> schema key \u2014 conditional complete screen HTML (supports {field_key} interpolation)<\/li>\n<li>Added: Diagnosis\/calculator mode \u2014 omit <code>to<\/code> with <code>complete_html_rules<\/code> for no-email chatbot<\/li>\n<li>Added: chatbot + <code>show_if<\/code> \/ <code>required_if<\/code> conditional fields now fully supported<\/li>\n<li>Improved: chatbot UI redesigned with LINE\/Slack-style header, timestamps, and bubble shadows<\/li>\n<li>Added: <code>pattern<\/code> \/ <code>minlength<\/code> \/ <code>maxlength<\/code> \/ <code>error_message<\/code> schema keys for field-level validation<\/li>\n<li>Added: <code>hxfe_validate_field<\/code> filter for per-field custom validation<\/li>\n<li>Added: <code>hxfe_validate_form<\/code> filter for cross-field validation (password confirm, at-least-one, etc.)<\/li>\n<li>Added: <code>before_html<\/code> \/ <code>after_html<\/code> schema keys for injecting HTML around fields<\/li>\n<li>Added: <code>disable_context<\/code> schema key to opt out of page slug auto-injection<\/li>\n<\/ul>\n\n<h4>1.3.3<\/h4>\n\n<ul>\n<li>Renamed: SHFE \u2192 HXFE across all PHP\/JS\/CSS (class names, IDs, function prefixes)<\/li>\n<li>Fixed: Scroll after htmx outerHTML swap (re-fetch element by ID after swap)<\/li>\n<li>Fixed: Step form Back button returning 403 (nonce was missing from hx-vals)<\/li>\n<li>Fixed: chatbot phpcs:ignore comment appearing as visible text in UI<\/li>\n<li>Fixed: Copy button fallback for HTTP environments (execCommand)<\/li>\n<li>Changed: Shortcode copy buttons labeled \"Form\" \/ \"iFrame\" for clarity<\/li>\n<\/ul>\n\n<h4>1.3.2<\/h4>\n\n<ul>\n<li>Added: IP restriction \u2014 whitelist IPs and CIDR ranges per form (<code>allowed_ips<\/code>)<\/li>\n<li>Added: Customizable IP blocked message (<code>ip_blocked_html<\/code>)<\/li>\n<li>Added: Form-level password authentication (<code>auth.users<\/code>)<\/li>\n<li>Added: Brute-force protection \u2014 lockout after 5 failed attempts (15 min)<\/li>\n<li>Added: Auth session via secure httponly samesite=strict cookie<\/li>\n<li>Added: Login form labels fully customizable per form<\/li>\n<li>Added: Support for wp-config.php constants as password source (keeps secrets out of Git)<\/li>\n<\/ul>\n\n<h4>1.3.1<\/h4>\n\n<ul>\n<li>Security: file field now uses HXFE's own safe MIME whitelist by default instead of WordPress defaults<\/li>\n<li>Security: added .htaccess to hxfe-uploads\/ directory to prevent PHP execution<\/li>\n<li>Added: file field type now works \u2014 uploaded files are attached to admin notification emails<\/li>\n<li>Added: includes\/file-upload.php with wp_handle_upload() based processing<\/li>\n<li>Added: mime_types schema key to whitelist allowed MIME types server-side<\/li>\n<li>Added: temporary files are automatically deleted after email is sent<\/li>\n<li>Removed: lint warning about file field being unimplemented<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>Refactored: extracted field renderer functions to includes\/fields\/field-renderers.php<\/li>\n<li>Refactored: added hxfe_validate_step_request() to DRY up step endpoint gate logic<\/li>\n<li>Refactored: improved PhpDoc type definitions on core functions (hxfe_process_fields, hxfe_eval_condition, hxfe_interpolate)<\/li>\n<\/ul>\n\n<h4>1.2.2<\/h4>\n\n<ul>\n<li>Fixed: confirm_label now correctly applies to the input form submit button<\/li>\n<li>Fixed: hxfe-front.js (scroll, focus, loading state) was not enqueued<\/li>\n<li>Fixed: disable_default_css and custom_css now work correctly<\/li>\n<li>Fixed: chatbot.js is now only loaded on pages with chatbot forms<\/li>\n<li>Fixed: uninstall.php now removes all plugin settings from the database<\/li>\n<li>Added: error_message schema key to customize the validation error summary text<\/li>\n<li>Added: confirm_label schema key (separate from submit_label for clarity)<\/li>\n<li>Note: file field type renders HTML but file saving is not yet implemented<\/li>\n<\/ul>\n\n<h4>1.2.1<\/h4>\n\n<ul>\n<li>Added tel and url field types with built-in validation<\/li>\n<li>Added Webhook support (Zapier, Make, Slack, custom HTTP endpoints)<\/li>\n<li>Added label customization: submit_label, back_label, next_label, confirm_heading<\/li>\n<li>Fixed confirmation screen: radio\/select now shows label instead of value<\/li>\n<li>Fixed confirmation screen: checkbox_group shows comma-separated labels<\/li>\n<li>Fixed confirmation screen: hidden fields (show_if=false) are now excluded<\/li>\n<li>Fixed mail body: empty fields and hidden fields are now excluded<\/li>\n<li>Added confirm: false support for step forms<\/li>\n<li>Added built-in placeholders: {site_name}, {site_url}, {date}, {time}<\/li>\n<li>Added admin form list page with lint warnings and shortcode copy buttons<\/li>\n<li>Strengthened schema lint: all 15 field types, cascade_from, chatbot bot_message<\/li>\n<li>Added wp_mail() failure logging when WP_DEBUG is enabled<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>Added field types: radio, checkbox_group, number, date, file<\/li>\n<li>Added chatbot UI mode (step_mode: chatbot) with typing animation<\/li>\n<li>Added 4 completion patterns: message, custom HTML, redirect, redirect_rules<\/li>\n<li>Added default values (value key on any field)<\/li>\n<li>Added multiple recipients (to as array)<\/li>\n<li>Added confirm: false for immediate submission<\/li>\n<li>Added cascade select (cascade_from \/ cascade_options)<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Added step forms (groups and one_by_one mode)<\/li>\n<li>Added reCAPTCHA v2 and v3<\/li>\n<li>Added privacy policy field<\/li>\n<li>Added auto-reply email<\/li>\n<li>Added SMTP configuration (Gmail, SendGrid, Mailgun, custom)<\/li>\n<li>Added iframe embedding with CORS support<\/li>\n<li>Added conditional logic (show_if, required_if, skip_if, to_rules, subject_rules)<\/li>\n<li>Added CSS customization options<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<li>Schema-driven form rendering<\/li>\n<li>htmx-powered input \u2192 confirm \u2192 complete flow<\/li>\n<li>Admin notification email and auto-reply<\/li>\n<li>Honeypot spam protection<\/li>\n<\/ul>","raw_excerpt":"Define forms as PHP arrays. Contact forms, step forms, chatbots, and surveys \u2014 all from one schema, no GUI required.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/323441","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=323441"}],"author":[{"embeddable":true,"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/youheiokubo"}],"wp:attachment":[{"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=323441"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=323441"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=323441"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=323441"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=323441"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/mlt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=323441"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}