Filter hooks are the primary extension seam of the Havenlytics real-estate plugin. Every filter lets you intercept a value the plugin is about to use — a query argument array, a block of property data, a template path, an email subject, a capability string — modify it, and hand it back. This reference documents every apply_filters() hook that ships in Havenlytics 3.3.1, grouped by subsystem, with the exact hook name, the file that fires it, the value you are expected to return, and the parameters your callback receives.
This page is part of the Havenlytics developer documentation. If you are looking for side-effect hooks (registration, notifications, lifecycle events) rather than value transformers, see the companion Action Hooks Reference. Together they cover the roughly 185 hooks Havenlytics exposes.
How Havenlytics filters work
A filter is registered with the core WordPress function add_filter(). Havenlytics calls apply_filters() at strategic points and passes the current value as the first argument, optionally followed by contextual arguments (a property ID, a data array, a context string). Your callback must return a value — that returned value replaces the original and flows on to the next filter and ultimately to the plugin.
<?php
add_filter( 'hvnly_empty_price_text', function ( $text ) {
// Always return the (possibly modified) value.
return __( 'Contact for pricing', 'my-theme' );
} );
Warning: The single most common mistake with filters is forgetting to
returnthe value. A callback that ends without returning effectively returnsnull, wiping out the data Havenlytics was about to render. If your prices, images, or agent cards disappear after adding a filter, check that every code path in your callback returns a value.
Argument count and priority
The 3rd and 4th parameters of add_filter() are the priority (default 10; lower runs earlier) and the accepted argument count (default 1). Many Havenlytics filters pass more than one argument — for example hvnly_property_data passes both the data array and the property ID. To receive the extra context you must declare the argument count explicitly.
<?php
// The 4th argument, 2, tells WordPress to pass BOTH $data and $property_id.
add_filter( 'hvnly_property_data', function ( $data, $property_id ) {
$data['is_new'] = ( time() - get_post_time( 'U', true, $property_id ) ) < WEEK_IN_SECONDS;
return $data;
}, 10, 2 );
Note: Nearly every Havenlytics hook uses the
hvnly_prefix. There is exactly one exception in the filter set: the REST controller class map filterhvnlynab_rest_api_class_map, which uses thehvnlynab_prefix. Watch the spelling.
Settings & Configuration
These filters govern capability checks, service registration, and admin-facing configuration. The most important is hvnly_admin_capability, which controls which WordPress capability is required to reach Havenlytics admin screens and REST endpoints — it defaults to manage_options.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_admin_capability | Menu.php, DocumentationPage.php, AnalyticsAPI.php, AgentIdentityHealthAdminPage.php, InquiryAdminPage.php, InquiryReplyService.php | Capability string required for admin/REST access (default manage_options) | string $capability |
hvnly_frontend_services | Frontend.php:104 | Array of frontend service classes to instantiate | array $services |
hvnly_admin_services | Admin/Admin.php:49 | Array of admin service classes to instantiate | array $services |
hvnly_database_services | Database/Database.php:91 | Array of database service classes to instantiate | array $services |
hvnly_setup_is_havenlytics_realty_theme | OnboardingWizard.php:426 | Whether the active theme is the official Havenlytics Realty theme | bool $is_realty_theme |
hvnly_gutenberg_managed_post_types | PluginGutenbergSupport.php:55 | Array of post types whose Gutenberg support Havenlytics manages | array $post_types |
hvnly_metabox_validation_errors | Havenlytics_Type.php:914 | Array of validation errors collected from a metabox save | array $errors |
hvnly_field_options | field-options.php:150 | Array of selectable options for a field | array $options |
Security: Raising
hvnly_admin_capabilityto a stricter capability tightens who can reach Havenlytics settings and builder REST routes. Lowering it belowmanage_optionsexposes plugin configuration to non-administrators — do so only for a deliberate role-based access model, and never below a capability you fully trust.
Cache
The search subsystem is cache-heavy. These filters let you customize cache keys, short-circuit a lookup with a precomputed result, and tune how long results and sidebar terms are stored.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_search_cache_key | AjaxHandler.php:660 | Cache key string for a search result set | string $cache_key |
hvnly_get_cached_search_results | AjaxHandler.php:677 | Cached results (return non-null to short-circuit the query) | mixed $results |
hvnly_search_cache_duration | AjaxHandler.php:692 | Cache lifetime in seconds for search results | int $duration |
hvnly_sidebar_cache_duration | SidebarSearchFilters.php:437 | Cache lifetime in seconds for sidebar filter data | int $duration |
Performance: Increasing
hvnly_search_cache_durationreduces database load on high-traffic archives but delays how quickly newly published listings appear in filtered results. Balance the TTL against how often your inventory changes.
Property Query & Search
These are among the most useful filters in Havenlytics. hvnly_property_query_args is the canonical seam for injecting meta_query, tax_query, ordering, or pagination into the property loop — for archives, shortcodes, and Elementor widgets alike.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_property_query_args | PropertyQueryBuilder.php:233; PropertyQueryArgsBuilder.php:70 | WP_Query arguments for the property loop | array $query_args [, array $data] |
hvnly_elementor_load_more_query_args | PropertyQueryArgsBuilder.php:73 | Query args for the Elementor “load more” request | array $query_args |
hvnly_property_query_executor_args | PropertyQueryExecutor.php:124 | Final query args just before execution | array $query_args |
hvnly_property_query_cache_key | PropertyQueryCache.php | Cache key for a compiled property query | string $cache_key |
hvnly_property_query_cache_hit | PropertyQueryCache.php | Cached query result (return to short-circuit) | mixed $result |
hvnly_property_query_cache_ttl | PropertyQueryCache.php | Cache lifetime in seconds for a property query | int $ttl |
hvnly_sidebar_filter_data | SidebarSearchFilters.php | Assembled data for the search sidebar | array $data |
hvnly_sidebar_cached_terms | SidebarSearchFilters.php | Cached taxonomy terms for the sidebar | array $terms |
hvnly_sidebar_query_args | SidebarSearchFilters.php | Query args used to build sidebar facets | array $query_args |
hvnly_cached_terms | SearchFilters.php | Cached taxonomy terms for search filters | array $terms |
hvnly_search_filter_data | SearchFilters.php | Assembled search filter data | array $data |
hvnly_filter_sidebar_fields | Helpers.php | Field definitions shown in the sidebar | array $fields |
hvnly_filter_sidebar_current_values | Helpers.php | Current selected values for sidebar fields | array $values |
hvnly_filter_sidebar_unique_property_ids | Helpers.php | Unique property IDs used to compute facet counts | array $property_ids |
Worked example: add a meta_query with hvnly_property_query_args
Suppose properties store a featured meta flag and you want every archive and shortcode loop to show featured listings first, then filter to a specific city passed in your own data array.
<?php
add_filter( 'hvnly_property_query_args', function ( $query_args, $data = array() ) {
// Order featured listings ahead of the rest.
$query_args['meta_key'] = 'featured';
$query_args['orderby'] = array( 'meta_value_num' => 'DESC', 'date' => 'DESC' );
// Restrict to properties that have a price set.
$meta_query = isset( $query_args['meta_query'] ) ? $query_args['meta_query'] : array();
$meta_query[] = array(
'key' => 'price',
'value' => '',
'compare' => '!=',
);
$query_args['meta_query'] = $meta_query;
return $query_args;
}, 10, 2 );
Tip: Always merge into the existing
meta_query/tax_queryarrays instead of overwriting them. Other extensions — and Havenlytics’ own search facets — may have already added clauses that you would silently discard.
Property Data, Images & Pricing
These filters expose the resolved data for a single property: its full data array, individual meta, gallery images, and the price string. hvnly_property_data is the workhorse for injecting or overriding fields before templates render them; the pricing filters let you localize and reshape how prices display.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_property_data | template-functions.php:274 | Full resolved data array for a property | array $property_data, int $property_id |
hvnly_property_meta | template-functions.php:923 | Resolved meta values for a property | array $meta |
hvnly_property_views_data | property-functions.php:401 | View/impression data for a property | array $views_data |
hvnly_field_template_mapping | property-functions.php:69; PropertyCardRenderer.php:72 | Map of field type → template used to render each field | array $mapping |
hvnly_section_template_mapping | PropertyCardRenderer.php:55 | Map of section → template | array $mapping |
hvnly_property_image | Helpers.php | Resolved featured image markup/data | mixed $image |
hvnly_property_image_url | Helpers.php | Resolved featured image URL | string $url |
hvnly_property_gallery_images | Helpers.php | Gallery image set for a property | array $images |
hvnly_property_gallery_image_urls | Helpers.php | Gallery image URLs | array $urls |
hvnly_property_image_size | Helpers.php | Registered image size used for property thumbnails | string $size |
hvnly_property_placeholder_url | Helpers.php | Fallback image URL when a property has no image | string $url |
hvnly_price_resolver | Hvnly_Price_Resolver.php | Resolved price result array | array $result, int $property_id |
hvnly_empty_price_text | Hvnly_Price_Resolver.php | Text shown when no price is set | string $text |
hvnly_price_on_call_text | Hvnly_Price_Resolver.php | Text shown for “price on call” listings | string $text |
hvnly_price_is_placeholder_slug | Hvnly_Price_Resolver.php | Whether a slug represents a placeholder price | bool $is_placeholder |
hvnly_mortgage_mode | Helpers.php | Mortgage calculation mode | string $mode |
hvnly_mortgage_unavailable_message | Hvnly_Mortgage_Calculator_Widget.php:90 | Message shown when mortgage calc is unavailable | string $message |
hvnly_use_large_number_format | Helpers.php:1328 | Whether to format large numbers (e.g. 1.2M) | bool $use_large_format |
hvnly_currency_symbol | number-field templates | Currency symbol (default $) | string $symbol |
hvnly_select_field_display_value | select-field templates | Display value for a select field | string $display_value |
Worked example: inject a computed field with hvnly_property_data
Add a price-per-square-foot value to every property so your card and single templates (or a child filter) can display it, computed from the existing price and area meta.
<?php
add_filter( 'hvnly_property_data', function ( $property_data, $property_id ) {
$price = isset( $property_data['price'] ) ? (float) $property_data['price'] : 0;
$area = isset( $property_data['area'] ) ? (float) $property_data['area'] : 0;
if ( $price > 0 && $area > 0 ) {
$property_data['price_per_sqft'] = round( $price / $area, 2 );
}
return $property_data;
}, 10, 2 );
Worked example: remap a field template with hvnly_field_template_mapping
Havenlytics chooses a template per field type from a mapping array. To render your energy_rating field with a custom template instead of the default text renderer, remap it.
<?php
add_filter( 'hvnly_field_template_mapping', function ( $mapping ) {
// Route the 'energy_rating' field to a template your theme provides.
$mapping['energy_rating'] = 'fields/energy-rating';
return $mapping;
} );
Note: The template name you return is resolved through the Havenlytics template loader, so a matching file placed in
havenlytics/fields/energy-rating.phpinside your active or child theme will override the plugin default. See Template Overriding for the full resolution order.
Template Rendering & Paths
These filters control which template file Havenlytics loads and where it searches. They are the programmatic counterpart to dropping override files into your theme, and they let you redirect, replace, or relocate templates without shipping physical files.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_get_template | template-functions.php:131 | Resolved template to load | string $template |
hvnly_get_template_part_templates | template-functions.php:809 | Candidate template-part files, in priority order | array $templates |
hvnly_templates_location | template-functions.php:783 | Base directory Havenlytics searches for templates | string $location |
hvnly_template_path | TemplateLoader.php:85; WorkspaceTemplateLoader.php:82 | Theme sub-folder used for overrides (default havenlytics/) | string $template_path |
hvnly_default_path | TemplateLoader.php:94 | Plugin fallback template directory | string $default_path |
hvnly_locate_template | TemplateLoader.php:159 | Final located template file path | string $located, array $template_names, string $template_path, string $default_path |
hvnly_use_new_single_renderer | template-functions.php:1314 | Whether to use the new single-property renderer (default true) | bool $use_new |
hvnly_pagination_type | template-functions.php:1348 | Pagination style for archives/loops | string $type |
hvnly_page_title | template-functions.php:1580 | Rendered page/archive title | string $title |
hvnly_show_page_title | archive/section-title.php:49 | Whether to display the page title (default true) | bool $show |
Layout
Layout filters expose the CSS class strings and structural decisions Havenlytics makes for the grid, main content column, and sidebar — plus whether a sidebar is shown at all in a given context.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_layout_grid_classes | template-hook-functions.php | CSS classes for the layout grid wrapper | string|array $classes |
hvnly_main_content_classes | template-hook-functions.php | CSS classes for the main content column | string|array $classes |
hvnly_sidebar_classes | template-hook-functions.php | CSS classes for the sidebar column | string|array $classes |
hvnly_should_display_sidebar | template-hook-functions.php:268; layout-functions.php:63 | Whether the sidebar renders in this context | bool $should, string $context |
hvnly_content_wrapper_classes | global/wrapper-start.php | CSS classes for the content wrapper | string|array $classes |
hvnly_sidebar_wrapper_classes | global/wrapper-start.php | CSS classes for the sidebar wrapper | string|array $classes |
hvnly_single_property_sidebar_id | LayoutManager.php | Registered sidebar ID for single property pages | string $sidebar_id |
hvnly_archive_sidebar_id | LayoutManager.php | Registered sidebar ID for archives | string $sidebar_id |
hvnly_sidebar_has_widgets | LayoutManager.php | Whether the resolved sidebar has active widgets | bool $has_widgets |
hvnly_layout_config | LayoutManager.php | Resolved layout configuration array | array $config |
hvnly_layout_breakpoints | LayoutManager.php | Responsive breakpoint definitions | array $breakpoints |
Shortcodes, Archive & Card Rendering
These filters cover default shortcode attributes, legacy attribute conversion, and card-level rendering.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_property_list_default_atts | PropertyList.php:67 | Default attributes for the property-list shortcode | array $atts |
hvnly_property_grid_default_atts | PropertyGrid.php:67 | Default attributes for the property-grid shortcode | array $atts |
hvnly_legacy_attribute_conversion | LegacyCompatibility.php:141 | Converted attribute set for legacy shortcodes | array $atts |
hvnly_agency_card_excerpt | partials/cards/agency-card.php:111 | Excerpt text rendered on an agency card | string $excerpt |
Note: For the shortcode markup itself and available attributes, see Shortcodes. The default-atts filters above run before the user’s shortcode attributes are merged, so user-supplied values still take precedence.
Agents & Agencies
Havenlytics resolves agents for properties, builds agent/agency archives, and renders agent cards. These filters let you change the rewrite slug, swap the repository class, adjust per-page counts, and reshape the resolved agent/agency data and card metadata.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_agent_rewrite_slug | AgentPostType.php:46 | URL slug for the agent CPT (default agent) | string $slug |
hvnly_agent_repository_class | AgentBootstrap.php:141 | Class name used as the agent repository | string $class |
hvnly_agent_archive_per_page | AgentArchiveQuery.php:65 | Agents per page on the agent archive | int $per_page |
hvnly_agency_archive_per_page | AgencyArchiveQuery.php:108 | Agencies per page on the agency archive | int $per_page |
hvnly_agency_archive_profile | AgencyArchiveQuery.php:140 | Agency profile data for the archive | array $profile |
hvnly_agency_properties_query_args | AgencyPropertiesQuery.php:49 | Query args for an agency’s properties | array $query_args |
hvnly_agent_properties_query_args | AgentPropertiesQuery.php:135 | Query args for an agent’s properties | array $query_args |
hvnly_agent_assigned_property_ids | AgentPropertiesQuery.php:101 | Property IDs assigned to an agent | array $property_ids |
hvnly_property_assigned_agent_ids | PropertyAgentResolver.php:61 | Agent IDs assigned to a property | array $agent_ids |
hvnly_property_agents | PropertyAgentResolver.php:109,114 | Resolved agents for a property | array $agents, int $property_id |
hvnly_property_legacy_user_agent | PropertyAgentResolver.php:254 | Legacy WP-user-based agent fallback | mixed $agent |
hvnly_agent_profile | AgentRepository.php:93 | Resolved agent profile data | array $profile |
hvnly_agency_profile | AgencyFields.php:249 | Resolved agency profile data | array $profile |
hvnly_sidebar_property_agents | agent-functions.php | Agents shown in the property sidebar | array $agents |
hvnly_default_sidebar_contact | agent-functions.php | Default contact used when no agent is assigned | mixed $contact |
hvnly_property_archive_view_type | agent-functions.php | View type (grid/list) for the property archive | string $view_type |
hvnly_agent_availability_definitions | agent-functions.php | Available agent availability states | array $definitions |
hvnly_agent_availability_status | agent-functions.php | Resolved availability status for an agent | string $status |
hvnly_agent_availability_notice | agent-functions.php | Availability notice text | string $notice |
hvnly_agent_accepts_inquiries | agent-functions.php | Whether an agent accepts inquiries | bool $accepts |
hvnly_agent_card_badges | agent-functions.php | Badges displayed on an agent card | array $badges |
hvnly_agent_experience_label | agent-functions.php | Experience label text for an agent | string $label |
hvnly_agent_location_label | agent-functions.php | Location label text for an agent | string $label |
Maps
The map subsystem supports multiple providers. hvnly_map_provider selects the active provider, and the remaining filters supply provider-specific credentials and tile configuration.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_map_provider | map-functions.php:29 | Active map provider identifier | string $provider |
hvnly_google_maps_api_key | map-functions.php | Google Maps API key | string $api_key |
hvnly_google_map_id | map-functions.php | Google Map style ID | string $map_id |
hvnly_update_map_settings | map-functions.php | Resolved map settings array | array $settings |
hvnly_osm_tile_url | map-functions.php | OpenStreetMap tile URL template | string $tile_url |
hvnly_osm_attribution | map-functions.php | OpenStreetMap attribution string | string $attribution |
Worked example: switch the map provider with hvnly_map_provider
Force OpenStreetMap everywhere (avoiding Google Maps billing) and point tiles at your own cached tile server.
<?php
add_filter( 'hvnly_map_provider', function ( $provider ) {
return 'osm';
} );
add_filter( 'hvnly_osm_tile_url', function ( $tile_url ) {
return 'https://tiles.example.com/{z}/{x}/{y}.png';
} );
add_filter( 'hvnly_osm_attribution', function ( $attribution ) {
return '© OpenStreetMap contributors';
} );
Security: Do not hard-code a production Google Maps API key in a public repository via
hvnly_google_maps_api_key. Return the value from a constant defined inwp-config.phpor an environment variable instead, and restrict the key by HTTP referrer in the Google Cloud console.
Import & Media
The import wizard uses these filters when resolving bundled or placeholder media and deciding whether remote media fetching is permitted.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_persist_card_builder_defaults | PropertyImportWizard.php:511 | Whether to persist card-builder defaults during import (default false) | bool $persist |
hvnly_import_local_bundled_attachment_id | PropertyImportWizard.php:3960 | Attachment ID for a locally bundled image | int $attachment_id |
hvnly_import_placeholder_attachment_id | PropertyImportWizard.php:3973 | Attachment ID for the import placeholder image | int $attachment_id |
hvnly_import_remote_media_available | utility-functions.php:37 | Whether remote media fetching is available | bool $available |
Note: The demo-import engine lives in
PropertyImportWizard.php; these filters only tune media resolution, not the import flow itself. See Import & Export for the wizard architecture.
Avatar
Avatars route through a single service (uploaded photo → Gravatar → Havenlytics placeholder). These filters let you change the placeholder, override Gravatar detection, and rewrite the final resolved URL.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_avatar_placeholder_url | AvatarService.php:203 | Placeholder avatar URL used as the final fallback | string $url |
hvnly_user_has_gravatar | AvatarService.php:372 | Whether a user has a Gravatar image | bool $has_gravatar |
hvnly_resolve_avatar_url | AvatarService.php:509 | Final resolved avatar URL | string $url, int $agent_id, int $user_id |
REST / AJAX
Havenlytics registers its REST controllers from a class-map array. The filter below is the seam for registering your own controller classes into the hvnlynab/v1 admin namespace — note it uses the hvnlynab_ prefix.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnlynab_rest_api_class_map | Api/Controller.php:55 | Map of REST controller classes to register | array $class_map |
hvnly_is_system_notification_context | utility-functions.php:118 | Whether the current request is a system notification context | bool $is_system |
Note: Havenlytics exposes two REST namespaces —
hvnlynab/v1for admin/settings/builders andhvnly/v1for the Agent Workspace SPA. Thehvnlynab_rest_api_class_mapfilter registers controllers into the admin namespace. See the REST API guide for route details.
Workspace
The Agent Workspace is a front-end SPA (namespace hvnly/v1). These filters govern its shortcode output, template resolution, settings, routing, registration behavior, redirects, localization, and the shape of API responses.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_workspace_shortcode_output | WorkspaceShortcode.php | Rendered workspace shortcode HTML | string $output |
hvnly_workspace_unavailable_template_args | WorkspaceTemplateLoader.php | Args passed to the “unavailable” template | array $args |
hvnly_workspace_template_path | WorkspaceTemplateLoader.php:82 | Theme sub-folder for workspace template overrides | string $template_path |
hvnly_workspace_default_path | WorkspaceTemplateLoader.php | Plugin fallback directory for workspace templates | string $default_path |
hvnly_workspace_locate_template | WorkspaceTemplateLoader.php | Final located workspace template path | string $located |
hvnly_workspace_allowed_templates | WorkspaceTemplateLoader.php | Whitelist of loadable workspace templates | array $templates |
hvnly_workspace_settings | WorkspaceSettings.php | Resolved workspace settings array | array $settings |
hvnly_workspace_enabled | WorkspaceBootstrap.php | Whether the workspace is enabled | bool $enabled |
hvnly_workspace_clean_routing | WorkspaceBootstrap.php | Whether clean (non-query-string) routing is used | bool $clean |
hvnly_workspace_agents_can_direct_publish | WorkspaceSettings.php | Whether agents may publish without review | bool $can_publish |
hvnly_workspace_registration_mode | WorkspaceSettings.php | Self-registration mode | string $mode |
hvnly_workspace_default_registration_role | WorkspaceSettings.php | Default role assigned on registration | string $role |
hvnly_workspace_logout_redirect | WorkspaceBootstrap.php | Redirect URL after logout | string $url |
hvnly_workspace_ensure_page | WorkspacePage.php | Whether to auto-create the workspace page | bool $ensure |
hvnly_workspace_redirect_admins_to_wpadmin | WorkspaceBootstrap.php | Whether admins are redirected to wp-admin | bool $redirect |
hvnly_workspace_admin_login_redirect | WorkspaceBootstrap.php | Redirect URL for admin login | string $url |
hvnly_workspace_timezone | WorkspaceSettings.php | Timezone used in the workspace | string $timezone |
hvnly_workspace_should_enqueue | WorkspaceAssets.php | Whether to enqueue workspace assets | bool $should |
hvnly_workspace_localize_data | WorkspaceAssets.php | Data localized to the SPA bootstrap | array $data |
hvnly_workspace_debug | WorkspaceBootstrap.php | Whether workspace debug mode is on | bool $debug |
hvnly_workspace_property_preview_url | WorkspaceBootstrap.php | Preview URL for a workspace property | string $url |
hvnly_workspace_me_response | MeController.php | Payload returned by the /me endpoint | array $response |
hvnly_workspace_inquiry_agent_ids | InquiryAgentResolver.php | Agent IDs resolved for a workspace inquiry | array $agent_ids |
hvnly_workspace_agent_admin_redirect | AgentAdminChrome.php | Redirect URL for agents reaching wp-admin | string $url |
Workspace Auth, Identity & Provisioning
These filters drive workspace authentication, the identity resolution pipeline, permission calculation, and the pluggable verification-factor system. The most important extension seam is hvnly_identity_factors, which lets you register a VerificationFactorInterface implementation into the identity-verification pipeline.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_workspace_identity | AgentIdentityService.php:75 | Resolved identity object/array for the current user | mixed $identity |
hvnly_workspace_identity_role | AgentIdentityService.php:427 | Role assigned to the resolved identity | string $role |
hvnly_workspace_permissions | PortalAuthorization.php:152 | Permission set granted to the workspace user | array $permissions |
hvnly_workspace_soft_portal_access | PortalAuthorization.php:219 | Whether soft (non-hard) portal access is granted | bool $access |
hvnly_workspace_soft_capability | PortalAuthorization.php:365 | Soft capability decision | bool $can |
hvnly_agent_publish_send_password_setup | AgentIdentityAdminBridge.php:182 | Whether to send a password-setup email on agent publish | bool $send |
hvnly_send_workspace_account_reset_email | AgentProvisioner.php:700 | Whether to send the account reset email | bool $send |
hvnly_identity_factors | IdentityVerificationService.php:73 | Registered verification factors (primary extension seam) | array $factors |
hvnly_identity_verification_satisfied | IdentityVerificationService.php:114 | Whether identity verification is satisfied | bool $satisfied |
hvnly_identity_audit_entry | IdentityVerificationAudit.php:150 | Audit log entry before it is written | array $entry |
hvnly_verification_client_ip | VerificationRateLimiter.php:82 | Client IP used for verification rate limiting | string $ip |
Note:
hvnly_identity_factorsis the designed seam for the email-verification architecture — register a class implementingVerificationFactorInterfaceand the identity service will run it as part of verification. See Authentication for the factor interface and the workspace auth flow.
Security:
hvnly_verification_client_ipfeeds the verification rate limiter. If you sit behind a reverse proxy or CDN, return the real client IP from a trusted forwarded header — but only trust that header when the request genuinely originates from your proxy, or an attacker can spoof it to bypass rate limits.
Email Verification
These filters control whether email verification is enforced, its token lifetime, and the subject and body of the verification email.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_email_verification_enforced | EmailVerificationFactor.php:86 | Whether email verification is enforced | bool $enforced |
hvnly_email_verification_ttl | EmailVerificationFactor.php:163 | Verification token lifetime in seconds (default DAY_IN_SECONDS) | int $ttl |
hvnly_email_verification_subject | EmailVerificationNotifier.php:107,181 | Subject line of the verification email | string $subject |
hvnly_email_verification_content | EmailVerificationNotifier.php:244 | Body content of the verification email | string $content |
Email Pipeline & Notifiers
The core email pipeline exposes filters for render context, final HTML, headers, merge tags, template registration, and branding — plus per-notifier filters for property-workflow, import-success, workspace-account, and registration emails.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_email_render_context | EmailRenderer.php:48 | Context array passed to an email template | array $context |
hvnly_email_html | EmailRenderer.php:62 | Final rendered email HTML | string $html |
hvnly_email_from_header | EmailHeaders.php:99 | The From: header for outgoing email | string $from_header |
hvnly_email_import_success_context | EmailContextBuilder.php:63 | Context for the import-success email | array $context |
hvnly_email_merge_tags | EmailContextBuilder.php:116 | Merge tags available to email templates | array $merge_tags |
hvnly_email_templates | EmailConstants.php:136 | Registered email templates | array $templates |
hvnly_email_template_labels | EmailConstants.php:175 | Human-readable labels for email templates | array $labels |
hvnly_email_logo_url | EmailBranding.php | Logo URL used in email branding | string $url |
hvnly_email_support_url | EmailBranding.php | Support URL in email branding | string $url |
hvnly_email_docs_url | EmailBranding.php | Docs URL in email branding | string $url |
hvnly_email_branding_context | EmailBranding.php | Branding context array for emails | array $context |
hvnly_email_property_workflow_enabled | PropertyWorkflowNotifier.php:143 | Whether property-workflow emails are enabled | bool $enabled |
hvnly_email_property_workflow_context | PropertyWorkflowNotifier.php:419 | Context for the property-workflow email | array $context |
hvnly_email_property_workflow_subject | PropertyWorkflowNotifier.php:448 | Subject of the property-workflow email | string $subject |
hvnly_email_property_admin_alert_email | PropertyWorkflowNotifier.php:477 | Recipient address for the property admin alert | string $email |
hvnly_email_import_success_subject | PropertyImportSuccessNotifier.php:147 | Subject of the import-success email | string $subject |
hvnly_email_import_success_headers | PropertyImportSuccessNotifier.php:170 | Headers for the import-success email | array $headers |
hvnly_workspace_account_email_subject | WorkspaceAccountNotifier.php:465 | Subject of the workspace-account email | string $subject |
hvnly_workspace_registration_email_context | RegistrationEmailNotifier.php | Context for the registration email | array $context |
hvnly_workspace_registration_email_subject | RegistrationEmailNotifier.php | Subject of the registration email | string $subject |
hvnly_workspace_registration_email_admin_alert_email | RegistrationEmailNotifier.php | Recipient of the registration admin alert | string $email |
hvnly_workspace_registration_email_is_admin_provisioned | RegistrationEmailNotifier.php | Whether the account was admin-provisioned | bool $is_admin_provisioned |
Worked example: rebrand outgoing email with hvnly_email_from_header
<?php
add_filter( 'hvnly_email_from_header', function ( $from_header ) {
return 'From: My Agency <listings@myagency.example>';
} );
add_filter( 'hvnly_email_logo_url', function ( $url ) {
return 'https://myagency.example/wp-content/uploads/brand/logo.png';
} );
Contact Agent (Inquiries)
The Contact Agent module powers property inquiry forms, spam protection, rate limiting, agent resolution, and reply email. These filters cover the entire inquiry lifecycle from button rendering through validation, notification, and admin reply.
| Hook | File | Filtered value / return | Parameters |
|---|---|---|---|
hvnly_contact_agent_enabled | contact-agent-functions.php:49 | Whether the Contact Agent module is enabled | bool $enabled |
hvnly_contact_agent_service_classes | ContactAgentModule.php:145 | Service classes the module instantiates | array $classes |
hvnly_contact_agent_button_args | contact-agent-functions.php | Args for the contact-agent button | array $args |
hvnly_contact_agent_client_ip | RateLimiter.php | Client IP used for inquiry rate limiting | string $ip |
hvnly_contact_agent_notify_agent | ContactAgent/* | Whether to notify the agent | bool $notify |
hvnly_contact_agent_notify_admin | ContactAgent/* | Whether to notify the admin | bool $notify |
hvnly_contact_agent_notify_sender | ContactAgent/* | Whether to send a confirmation to the sender | bool $notify |
hvnly_contact_agent_success_message | ContactAgent/* | Success message shown after submission | string $message |
hvnly_contact_agent_admin_notification_email | ContactAgent/* | Recipient address for admin notifications | string $email |
hvnly_contact_agent_honeypot_enabled | ContactAgent/* | Whether the honeypot spam field is enabled | bool $enabled |
hvnly_contact_agent_rate_limit_max | RateLimiter.php | Max submissions allowed per window | int $max |
hvnly_contact_agent_rate_limit_window | RateLimiter.php | Rate-limit window in seconds | int $window |
hvnly_contact_agent_spam_verify | SpamGuard.php:52 | Spam verification decision | bool $is_valid |
hvnly_contact_agent_validated_payload | InquiryValidator.php:200 | Validated inquiry payload | array $payload |
hvnly_contact_agent_email_templates | ContactAgent/* | Email templates for inquiries | array $templates |
hvnly_contact_agent_email_subject | ContactAgent/* | Inquiry email subject | string $subject |
hvnly_contact_agent_email_headers | ContactAgent/* | Inquiry email headers | array $headers |
hvnly_contact_agent_sender_email_headers | ContactAgent/* | Headers for the sender confirmation email | array $headers |
hvnly_contact_agent_email_render_context | ContactAgent/* | Render context for inquiry email | array $context |
hvnly_contact_agent_email_html | ContactAgent/* | Final inquiry email HTML | string $html |
hvnly_contact_agent_resolved_agent | ContactAgent/* | Resolved agent for an inquiry | mixed $agent |
hvnly_contact_agent_resolved_agent_profile | ContactAgent/* | Resolved agent profile for an inquiry | array $profile |
hvnly_contact_agent_email_context | ContactAgent/* | Context array for inquiry email | array $context |
hvnly_contact_agent_reply_email_context | ContactAgent/* | Context for the reply email | array $context |
hvnly_contact_agent_email_merge_tags | ContactAgent/* | Merge tags for inquiry emails | array $merge_tags |
hvnly_contact_agent_admin_reply_subject | ContactAgent/* | Subject of the admin reply email | string $subject |
hvnly_contact_agent_admin_reply_headers | ContactAgent/* | Headers of the admin reply email | array $headers |
hvnly_inquiry_reply_capability | InquiryReplyService.php | Capability required to reply to inquiries | string $capability |
hvnly_inquiry_reply_user_can_reply | InquiryReplyService.php | Whether a user can reply to an inquiry | bool $can_reply |
hvnly_inquiry_reply_min_length | InquiryReplyService.php | Minimum reply length | int $min_length |
hvnly_inquiry_reply_max_length | InquiryReplyService.php | Maximum reply length | int $max_length |
hvnly_inquiry_reply_rate_limit_max | InquiryReplyService.php | Max replies allowed per window | int $max |
hvnly_inquiry_reply_rate_limit_window | InquiryReplyService.php | Reply rate-limit window in seconds | int $window |
Security: When you return
truefromhvnly_contact_agent_spam_verifyto accept a submission, you are overriding Havenlytics’ built-in spam checks. Never blanket-approve inquiries; run your own validation (reCAPTCHA, Akismet) inside the callback and return its verdict.
Best practices
- Always return a value. Every branch of a filter callback must return — returning nothing returns
nulland erases the data. - Declare the correct argument count. If a filter passes extra parameters (like
$property_id), pass10, 2(or the right count) toadd_filter(), or your callback silently receives only the first argument. - Do not change the type. Return the same type the filter provides — an array stays an array, a bool stays a bool, a string stays a string. Returning the wrong type breaks downstream code that does not re-check.
- Merge, do not overwrite. For array filters like
hvnly_property_query_argsandhvnly_field_template_mapping, add your keys to the incoming array rather than replacing it wholesale. - Namespace your logic. Guard expensive work with context checks so your callback only runs where it should (e.g.
is_singular( 'hvnly_property' )). - Prefer the documented seams. For identity, use
hvnly_identity_factors; for query changes, usehvnly_property_query_args; for template swaps, use the template loader filters plus theme overrides.
Performance
- Filters such as
hvnly_property_data,hvnly_property_agents, and the layout class filters run inside loops — once per property or per card. Keep their callbacks cheap and avoid database queries inside them; if you must query, cache the result outside the loop. - Use the cache-key and TTL filters (
hvnly_search_cache_key,hvnly_search_cache_duration,hvnly_property_query_cache_ttl) to tune the trade-off between freshness and database load rather than disabling caching. - Return early. If your modification only applies to a specific view or post type, check context first and return the unmodified value immediately otherwise.
Security
- Escape on output, not inside data filters — but if a filter feeds directly into markup (email HTML, class strings, URLs), sanitize with the appropriate function (
esc_url,esc_attr,wp_kses_post) before returning. - Treat capability and access filters (
hvnly_admin_capability,hvnly_workspace_permissions,hvnly_workspace_soft_portal_access,hvnly_inquiry_reply_capability) as security boundaries — never loosen them without a deliberate, audited access model. - IP-derived filters (
hvnly_verification_client_ip,hvnly_contact_agent_client_ip) feed rate limiters; only trust forwarded headers from infrastructure you control.
Common mistakes
- Forgetting to return the value. The number-one filter bug. If content vanishes after you add a filter, you almost certainly missed a
return. - Wrong argument count. Expecting
$property_idinhvnly_property_databut omitting, 10, 2— the parameter arrives asnull. - Confusing filters with actions. A filter must return; an action must not. Do not hook side-effect logic to a filter and forget the return, and do not try to modify-and-return from an action.
- Misspelling the prefix. All but one filter use
hvnly_; onlyhvnlynab_rest_api_class_mapuseshvnlynab_. - Overwriting arrays. Replacing an incoming
meta_queryor template mapping instead of merging into it, silently dropping other extensions’ contributions.