Skip to main content
Skip to main content

Developers Doc

Filter Hooks

24 mins read 93 Views 3+

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 return the value. A callback that ends without returning effectively returns null, 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 filter hvnlynab_rest_api_class_map, which uses the hvnlynab_ 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.

HookFileFiltered value / returnParameters
hvnly_admin_capabilityMenu.php, DocumentationPage.php, AnalyticsAPI.php, AgentIdentityHealthAdminPage.php, InquiryAdminPage.php, InquiryReplyService.phpCapability string required for admin/REST access (default manage_options)string $capability
hvnly_frontend_servicesFrontend.php:104Array of frontend service classes to instantiatearray $services
hvnly_admin_servicesAdmin/Admin.php:49Array of admin service classes to instantiatearray $services
hvnly_database_servicesDatabase/Database.php:91Array of database service classes to instantiatearray $services
hvnly_setup_is_havenlytics_realty_themeOnboardingWizard.php:426Whether the active theme is the official Havenlytics Realty themebool $is_realty_theme
hvnly_gutenberg_managed_post_typesPluginGutenbergSupport.php:55Array of post types whose Gutenberg support Havenlytics managesarray $post_types
hvnly_metabox_validation_errorsHavenlytics_Type.php:914Array of validation errors collected from a metabox savearray $errors
hvnly_field_optionsfield-options.php:150Array of selectable options for a fieldarray $options

Security: Raising hvnly_admin_capability to a stricter capability tightens who can reach Havenlytics settings and builder REST routes. Lowering it below manage_options exposes 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.

HookFileFiltered value / returnParameters
hvnly_search_cache_keyAjaxHandler.php:660Cache key string for a search result setstring $cache_key
hvnly_get_cached_search_resultsAjaxHandler.php:677Cached results (return non-null to short-circuit the query)mixed $results
hvnly_search_cache_durationAjaxHandler.php:692Cache lifetime in seconds for search resultsint $duration
hvnly_sidebar_cache_durationSidebarSearchFilters.php:437Cache lifetime in seconds for sidebar filter dataint $duration

Performance: Increasing hvnly_search_cache_duration reduces 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_querytax_query, ordering, or pagination into the property loop — for archives, shortcodes, and Elementor widgets alike.

HookFileFiltered value / returnParameters
hvnly_property_query_argsPropertyQueryBuilder.php:233; PropertyQueryArgsBuilder.php:70WP_Query arguments for the property looparray $query_args [, array $data]
hvnly_elementor_load_more_query_argsPropertyQueryArgsBuilder.php:73Query args for the Elementor “load more” requestarray $query_args
hvnly_property_query_executor_argsPropertyQueryExecutor.php:124Final query args just before executionarray $query_args
hvnly_property_query_cache_keyPropertyQueryCache.phpCache key for a compiled property querystring $cache_key
hvnly_property_query_cache_hitPropertyQueryCache.phpCached query result (return to short-circuit)mixed $result
hvnly_property_query_cache_ttlPropertyQueryCache.phpCache lifetime in seconds for a property queryint $ttl
hvnly_sidebar_filter_dataSidebarSearchFilters.phpAssembled data for the search sidebararray $data
hvnly_sidebar_cached_termsSidebarSearchFilters.phpCached taxonomy terms for the sidebararray $terms
hvnly_sidebar_query_argsSidebarSearchFilters.phpQuery args used to build sidebar facetsarray $query_args
hvnly_cached_termsSearchFilters.phpCached taxonomy terms for search filtersarray $terms
hvnly_search_filter_dataSearchFilters.phpAssembled search filter dataarray $data
hvnly_filter_sidebar_fieldsHelpers.phpField definitions shown in the sidebararray $fields
hvnly_filter_sidebar_current_valuesHelpers.phpCurrent selected values for sidebar fieldsarray $values
hvnly_filter_sidebar_unique_property_idsHelpers.phpUnique property IDs used to compute facet countsarray $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_query arrays 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.

HookFileFiltered value / returnParameters
hvnly_property_datatemplate-functions.php:274Full resolved data array for a propertyarray $property_dataint $property_id
hvnly_property_metatemplate-functions.php:923Resolved meta values for a propertyarray $meta
hvnly_property_views_dataproperty-functions.php:401View/impression data for a propertyarray $views_data
hvnly_field_template_mappingproperty-functions.php:69; PropertyCardRenderer.php:72Map of field type → template used to render each fieldarray $mapping
hvnly_section_template_mappingPropertyCardRenderer.php:55Map of section → templatearray $mapping
hvnly_property_imageHelpers.phpResolved featured image markup/datamixed $image
hvnly_property_image_urlHelpers.phpResolved featured image URLstring $url
hvnly_property_gallery_imagesHelpers.phpGallery image set for a propertyarray $images
hvnly_property_gallery_image_urlsHelpers.phpGallery image URLsarray $urls
hvnly_property_image_sizeHelpers.phpRegistered image size used for property thumbnailsstring $size
hvnly_property_placeholder_urlHelpers.phpFallback image URL when a property has no imagestring $url
hvnly_price_resolverHvnly_Price_Resolver.phpResolved price result arrayarray $resultint $property_id
hvnly_empty_price_textHvnly_Price_Resolver.phpText shown when no price is setstring $text
hvnly_price_on_call_textHvnly_Price_Resolver.phpText shown for “price on call” listingsstring $text
hvnly_price_is_placeholder_slugHvnly_Price_Resolver.phpWhether a slug represents a placeholder pricebool $is_placeholder
hvnly_mortgage_modeHelpers.phpMortgage calculation modestring $mode
hvnly_mortgage_unavailable_messageHvnly_Mortgage_Calculator_Widget.php:90Message shown when mortgage calc is unavailablestring $message
hvnly_use_large_number_formatHelpers.php:1328Whether to format large numbers (e.g. 1.2M)bool $use_large_format
hvnly_currency_symbolnumber-field templatesCurrency symbol (default $)string $symbol
hvnly_select_field_display_valueselect-field templatesDisplay value for a select fieldstring $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.php inside 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.

HookFileFiltered value / returnParameters
hvnly_get_templatetemplate-functions.php:131Resolved template to loadstring $template
hvnly_get_template_part_templatestemplate-functions.php:809Candidate template-part files, in priority orderarray $templates
hvnly_templates_locationtemplate-functions.php:783Base directory Havenlytics searches for templatesstring $location
hvnly_template_pathTemplateLoader.php:85; WorkspaceTemplateLoader.php:82Theme sub-folder used for overrides (default havenlytics/)string $template_path
hvnly_default_pathTemplateLoader.php:94Plugin fallback template directorystring $default_path
hvnly_locate_templateTemplateLoader.php:159Final located template file pathstring $locatedarray $template_namesstring $template_pathstring $default_path
hvnly_use_new_single_renderertemplate-functions.php:1314Whether to use the new single-property renderer (default true)bool $use_new
hvnly_pagination_typetemplate-functions.php:1348Pagination style for archives/loopsstring $type
hvnly_page_titletemplate-functions.php:1580Rendered page/archive titlestring $title
hvnly_show_page_titlearchive/section-title.php:49Whether 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.

HookFileFiltered value / returnParameters
hvnly_layout_grid_classestemplate-hook-functions.phpCSS classes for the layout grid wrapperstring|array $classes
hvnly_main_content_classestemplate-hook-functions.phpCSS classes for the main content columnstring|array $classes
hvnly_sidebar_classestemplate-hook-functions.phpCSS classes for the sidebar columnstring|array $classes
hvnly_should_display_sidebartemplate-hook-functions.php:268; layout-functions.php:63Whether the sidebar renders in this contextbool $shouldstring $context
hvnly_content_wrapper_classesglobal/wrapper-start.phpCSS classes for the content wrapperstring|array $classes
hvnly_sidebar_wrapper_classesglobal/wrapper-start.phpCSS classes for the sidebar wrapperstring|array $classes
hvnly_single_property_sidebar_idLayoutManager.phpRegistered sidebar ID for single property pagesstring $sidebar_id
hvnly_archive_sidebar_idLayoutManager.phpRegistered sidebar ID for archivesstring $sidebar_id
hvnly_sidebar_has_widgetsLayoutManager.phpWhether the resolved sidebar has active widgetsbool $has_widgets
hvnly_layout_configLayoutManager.phpResolved layout configuration arrayarray $config
hvnly_layout_breakpointsLayoutManager.phpResponsive breakpoint definitionsarray $breakpoints

Shortcodes, Archive & Card Rendering

These filters cover default shortcode attributes, legacy attribute conversion, and card-level rendering.

HookFileFiltered value / returnParameters
hvnly_property_list_default_attsPropertyList.php:67Default attributes for the property-list shortcodearray $atts
hvnly_property_grid_default_attsPropertyGrid.php:67Default attributes for the property-grid shortcodearray $atts
hvnly_legacy_attribute_conversionLegacyCompatibility.php:141Converted attribute set for legacy shortcodesarray $atts
hvnly_agency_card_excerptpartials/cards/agency-card.php:111Excerpt text rendered on an agency cardstring $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.

HookFileFiltered value / returnParameters
hvnly_agent_rewrite_slugAgentPostType.php:46URL slug for the agent CPT (default agent)string $slug
hvnly_agent_repository_classAgentBootstrap.php:141Class name used as the agent repositorystring $class
hvnly_agent_archive_per_pageAgentArchiveQuery.php:65Agents per page on the agent archiveint $per_page
hvnly_agency_archive_per_pageAgencyArchiveQuery.php:108Agencies per page on the agency archiveint $per_page
hvnly_agency_archive_profileAgencyArchiveQuery.php:140Agency profile data for the archivearray $profile
hvnly_agency_properties_query_argsAgencyPropertiesQuery.php:49Query args for an agency’s propertiesarray $query_args
hvnly_agent_properties_query_argsAgentPropertiesQuery.php:135Query args for an agent’s propertiesarray $query_args
hvnly_agent_assigned_property_idsAgentPropertiesQuery.php:101Property IDs assigned to an agentarray $property_ids
hvnly_property_assigned_agent_idsPropertyAgentResolver.php:61Agent IDs assigned to a propertyarray $agent_ids
hvnly_property_agentsPropertyAgentResolver.php:109,114Resolved agents for a propertyarray $agentsint $property_id
hvnly_property_legacy_user_agentPropertyAgentResolver.php:254Legacy WP-user-based agent fallbackmixed $agent
hvnly_agent_profileAgentRepository.php:93Resolved agent profile dataarray $profile
hvnly_agency_profileAgencyFields.php:249Resolved agency profile dataarray $profile
hvnly_sidebar_property_agentsagent-functions.phpAgents shown in the property sidebararray $agents
hvnly_default_sidebar_contactagent-functions.phpDefault contact used when no agent is assignedmixed $contact
hvnly_property_archive_view_typeagent-functions.phpView type (grid/list) for the property archivestring $view_type
hvnly_agent_availability_definitionsagent-functions.phpAvailable agent availability statesarray $definitions
hvnly_agent_availability_statusagent-functions.phpResolved availability status for an agentstring $status
hvnly_agent_availability_noticeagent-functions.phpAvailability notice textstring $notice
hvnly_agent_accepts_inquiriesagent-functions.phpWhether an agent accepts inquiriesbool $accepts
hvnly_agent_card_badgesagent-functions.phpBadges displayed on an agent cardarray $badges
hvnly_agent_experience_labelagent-functions.phpExperience label text for an agentstring $label
hvnly_agent_location_labelagent-functions.phpLocation label text for an agentstring $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.

HookFileFiltered value / returnParameters
hvnly_map_providermap-functions.php:29Active map provider identifierstring $provider
hvnly_google_maps_api_keymap-functions.phpGoogle Maps API keystring $api_key
hvnly_google_map_idmap-functions.phpGoogle Map style IDstring $map_id
hvnly_update_map_settingsmap-functions.phpResolved map settings arrayarray $settings
hvnly_osm_tile_urlmap-functions.phpOpenStreetMap tile URL templatestring $tile_url
hvnly_osm_attributionmap-functions.phpOpenStreetMap attribution stringstring $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 '&copy; 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 in wp-config.php or 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.

HookFileFiltered value / returnParameters
hvnly_persist_card_builder_defaultsPropertyImportWizard.php:511Whether to persist card-builder defaults during import (default false)bool $persist
hvnly_import_local_bundled_attachment_idPropertyImportWizard.php:3960Attachment ID for a locally bundled imageint $attachment_id
hvnly_import_placeholder_attachment_idPropertyImportWizard.php:3973Attachment ID for the import placeholder imageint $attachment_id
hvnly_import_remote_media_availableutility-functions.php:37Whether remote media fetching is availablebool $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.

HookFileFiltered value / returnParameters
hvnly_avatar_placeholder_urlAvatarService.php:203Placeholder avatar URL used as the final fallbackstring $url
hvnly_user_has_gravatarAvatarService.php:372Whether a user has a Gravatar imagebool $has_gravatar
hvnly_resolve_avatar_urlAvatarService.php:509Final resolved avatar URLstring $urlint $agent_idint $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.

HookFileFiltered value / returnParameters
hvnlynab_rest_api_class_mapApi/Controller.php:55Map of REST controller classes to registerarray $class_map
hvnly_is_system_notification_contextutility-functions.php:118Whether the current request is a system notification contextbool $is_system

Note: Havenlytics exposes two REST namespaces — hvnlynab/v1 for admin/settings/builders and hvnly/v1 for the Agent Workspace SPA. The hvnlynab_rest_api_class_map filter 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.

HookFileFiltered value / returnParameters
hvnly_workspace_shortcode_outputWorkspaceShortcode.phpRendered workspace shortcode HTMLstring $output
hvnly_workspace_unavailable_template_argsWorkspaceTemplateLoader.phpArgs passed to the “unavailable” templatearray $args
hvnly_workspace_template_pathWorkspaceTemplateLoader.php:82Theme sub-folder for workspace template overridesstring $template_path
hvnly_workspace_default_pathWorkspaceTemplateLoader.phpPlugin fallback directory for workspace templatesstring $default_path
hvnly_workspace_locate_templateWorkspaceTemplateLoader.phpFinal located workspace template pathstring $located
hvnly_workspace_allowed_templatesWorkspaceTemplateLoader.phpWhitelist of loadable workspace templatesarray $templates
hvnly_workspace_settingsWorkspaceSettings.phpResolved workspace settings arrayarray $settings
hvnly_workspace_enabledWorkspaceBootstrap.phpWhether the workspace is enabledbool $enabled
hvnly_workspace_clean_routingWorkspaceBootstrap.phpWhether clean (non-query-string) routing is usedbool $clean
hvnly_workspace_agents_can_direct_publishWorkspaceSettings.phpWhether agents may publish without reviewbool $can_publish
hvnly_workspace_registration_modeWorkspaceSettings.phpSelf-registration modestring $mode
hvnly_workspace_default_registration_roleWorkspaceSettings.phpDefault role assigned on registrationstring $role
hvnly_workspace_logout_redirectWorkspaceBootstrap.phpRedirect URL after logoutstring $url
hvnly_workspace_ensure_pageWorkspacePage.phpWhether to auto-create the workspace pagebool $ensure
hvnly_workspace_redirect_admins_to_wpadminWorkspaceBootstrap.phpWhether admins are redirected to wp-adminbool $redirect
hvnly_workspace_admin_login_redirectWorkspaceBootstrap.phpRedirect URL for admin loginstring $url
hvnly_workspace_timezoneWorkspaceSettings.phpTimezone used in the workspacestring $timezone
hvnly_workspace_should_enqueueWorkspaceAssets.phpWhether to enqueue workspace assetsbool $should
hvnly_workspace_localize_dataWorkspaceAssets.phpData localized to the SPA bootstraparray $data
hvnly_workspace_debugWorkspaceBootstrap.phpWhether workspace debug mode is onbool $debug
hvnly_workspace_property_preview_urlWorkspaceBootstrap.phpPreview URL for a workspace propertystring $url
hvnly_workspace_me_responseMeController.phpPayload returned by the /me endpointarray $response
hvnly_workspace_inquiry_agent_idsInquiryAgentResolver.phpAgent IDs resolved for a workspace inquiryarray $agent_ids
hvnly_workspace_agent_admin_redirectAgentAdminChrome.phpRedirect URL for agents reaching wp-adminstring $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.

HookFileFiltered value / returnParameters
hvnly_workspace_identityAgentIdentityService.php:75Resolved identity object/array for the current usermixed $identity
hvnly_workspace_identity_roleAgentIdentityService.php:427Role assigned to the resolved identitystring $role
hvnly_workspace_permissionsPortalAuthorization.php:152Permission set granted to the workspace userarray $permissions
hvnly_workspace_soft_portal_accessPortalAuthorization.php:219Whether soft (non-hard) portal access is grantedbool $access
hvnly_workspace_soft_capabilityPortalAuthorization.php:365Soft capability decisionbool $can
hvnly_agent_publish_send_password_setupAgentIdentityAdminBridge.php:182Whether to send a password-setup email on agent publishbool $send
hvnly_send_workspace_account_reset_emailAgentProvisioner.php:700Whether to send the account reset emailbool $send
hvnly_identity_factorsIdentityVerificationService.php:73Registered verification factors (primary extension seam)array $factors
hvnly_identity_verification_satisfiedIdentityVerificationService.php:114Whether identity verification is satisfiedbool $satisfied
hvnly_identity_audit_entryIdentityVerificationAudit.php:150Audit log entry before it is writtenarray $entry
hvnly_verification_client_ipVerificationRateLimiter.php:82Client IP used for verification rate limitingstring $ip

Note: hvnly_identity_factors is the designed seam for the email-verification architecture — register a class implementing VerificationFactorInterface and 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_ip feeds 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.

HookFileFiltered value / returnParameters
hvnly_email_verification_enforcedEmailVerificationFactor.php:86Whether email verification is enforcedbool $enforced
hvnly_email_verification_ttlEmailVerificationFactor.php:163Verification token lifetime in seconds (default DAY_IN_SECONDS)int $ttl
hvnly_email_verification_subjectEmailVerificationNotifier.php:107,181Subject line of the verification emailstring $subject
hvnly_email_verification_contentEmailVerificationNotifier.php:244Body content of the verification emailstring $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.

HookFileFiltered value / returnParameters
hvnly_email_render_contextEmailRenderer.php:48Context array passed to an email templatearray $context
hvnly_email_htmlEmailRenderer.php:62Final rendered email HTMLstring $html
hvnly_email_from_headerEmailHeaders.php:99The From: header for outgoing emailstring $from_header
hvnly_email_import_success_contextEmailContextBuilder.php:63Context for the import-success emailarray $context
hvnly_email_merge_tagsEmailContextBuilder.php:116Merge tags available to email templatesarray $merge_tags
hvnly_email_templatesEmailConstants.php:136Registered email templatesarray $templates
hvnly_email_template_labelsEmailConstants.php:175Human-readable labels for email templatesarray $labels
hvnly_email_logo_urlEmailBranding.phpLogo URL used in email brandingstring $url
hvnly_email_support_urlEmailBranding.phpSupport URL in email brandingstring $url
hvnly_email_docs_urlEmailBranding.phpDocs URL in email brandingstring $url
hvnly_email_branding_contextEmailBranding.phpBranding context array for emailsarray $context
hvnly_email_property_workflow_enabledPropertyWorkflowNotifier.php:143Whether property-workflow emails are enabledbool $enabled
hvnly_email_property_workflow_contextPropertyWorkflowNotifier.php:419Context for the property-workflow emailarray $context
hvnly_email_property_workflow_subjectPropertyWorkflowNotifier.php:448Subject of the property-workflow emailstring $subject
hvnly_email_property_admin_alert_emailPropertyWorkflowNotifier.php:477Recipient address for the property admin alertstring $email
hvnly_email_import_success_subjectPropertyImportSuccessNotifier.php:147Subject of the import-success emailstring $subject
hvnly_email_import_success_headersPropertyImportSuccessNotifier.php:170Headers for the import-success emailarray $headers
hvnly_workspace_account_email_subjectWorkspaceAccountNotifier.php:465Subject of the workspace-account emailstring $subject
hvnly_workspace_registration_email_contextRegistrationEmailNotifier.phpContext for the registration emailarray $context
hvnly_workspace_registration_email_subjectRegistrationEmailNotifier.phpSubject of the registration emailstring $subject
hvnly_workspace_registration_email_admin_alert_emailRegistrationEmailNotifier.phpRecipient of the registration admin alertstring $email
hvnly_workspace_registration_email_is_admin_provisionedRegistrationEmailNotifier.phpWhether the account was admin-provisionedbool $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.

HookFileFiltered value / returnParameters
hvnly_contact_agent_enabledcontact-agent-functions.php:49Whether the Contact Agent module is enabledbool $enabled
hvnly_contact_agent_service_classesContactAgentModule.php:145Service classes the module instantiatesarray $classes
hvnly_contact_agent_button_argscontact-agent-functions.phpArgs for the contact-agent buttonarray $args
hvnly_contact_agent_client_ipRateLimiter.phpClient IP used for inquiry rate limitingstring $ip
hvnly_contact_agent_notify_agentContactAgent/*Whether to notify the agentbool $notify
hvnly_contact_agent_notify_adminContactAgent/*Whether to notify the adminbool $notify
hvnly_contact_agent_notify_senderContactAgent/*Whether to send a confirmation to the senderbool $notify
hvnly_contact_agent_success_messageContactAgent/*Success message shown after submissionstring $message
hvnly_contact_agent_admin_notification_emailContactAgent/*Recipient address for admin notificationsstring $email
hvnly_contact_agent_honeypot_enabledContactAgent/*Whether the honeypot spam field is enabledbool $enabled
hvnly_contact_agent_rate_limit_maxRateLimiter.phpMax submissions allowed per windowint $max
hvnly_contact_agent_rate_limit_windowRateLimiter.phpRate-limit window in secondsint $window
hvnly_contact_agent_spam_verifySpamGuard.php:52Spam verification decisionbool $is_valid
hvnly_contact_agent_validated_payloadInquiryValidator.php:200Validated inquiry payloadarray $payload
hvnly_contact_agent_email_templatesContactAgent/*Email templates for inquiriesarray $templates
hvnly_contact_agent_email_subjectContactAgent/*Inquiry email subjectstring $subject
hvnly_contact_agent_email_headersContactAgent/*Inquiry email headersarray $headers
hvnly_contact_agent_sender_email_headersContactAgent/*Headers for the sender confirmation emailarray $headers
hvnly_contact_agent_email_render_contextContactAgent/*Render context for inquiry emailarray $context
hvnly_contact_agent_email_htmlContactAgent/*Final inquiry email HTMLstring $html
hvnly_contact_agent_resolved_agentContactAgent/*Resolved agent for an inquirymixed $agent
hvnly_contact_agent_resolved_agent_profileContactAgent/*Resolved agent profile for an inquiryarray $profile
hvnly_contact_agent_email_contextContactAgent/*Context array for inquiry emailarray $context
hvnly_contact_agent_reply_email_contextContactAgent/*Context for the reply emailarray $context
hvnly_contact_agent_email_merge_tagsContactAgent/*Merge tags for inquiry emailsarray $merge_tags
hvnly_contact_agent_admin_reply_subjectContactAgent/*Subject of the admin reply emailstring $subject
hvnly_contact_agent_admin_reply_headersContactAgent/*Headers of the admin reply emailarray $headers
hvnly_inquiry_reply_capabilityInquiryReplyService.phpCapability required to reply to inquiriesstring $capability
hvnly_inquiry_reply_user_can_replyInquiryReplyService.phpWhether a user can reply to an inquirybool $can_reply
hvnly_inquiry_reply_min_lengthInquiryReplyService.phpMinimum reply lengthint $min_length
hvnly_inquiry_reply_max_lengthInquiryReplyService.phpMaximum reply lengthint $max_length
hvnly_inquiry_reply_rate_limit_maxInquiryReplyService.phpMax replies allowed per windowint $max
hvnly_inquiry_reply_rate_limit_windowInquiryReplyService.phpReply rate-limit window in secondsint $window

Security: When you return true from hvnly_contact_agent_spam_verify to 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 null and erases the data.
  • Declare the correct argument count. If a filter passes extra parameters (like $property_id), pass 10, 2 (or the right count) to add_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_args and hvnly_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, use hvnly_property_query_args; for template swaps, use the template loader filters plus theme overrides.

Performance

  • Filters such as hvnly_property_datahvnly_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_keyhvnly_search_cache_durationhvnly_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_urlesc_attrwp_kses_post) before returning.
  • Treat capability and access filters (hvnly_admin_capabilityhvnly_workspace_permissionshvnly_workspace_soft_portal_accesshvnly_inquiry_reply_capability) as security boundaries — never loosen them without a deliberate, audited access model.
  • IP-derived filters (hvnly_verification_client_iphvnly_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_id in hvnly_property_data but omitting , 10, 2 — the parameter arrives as null.
  • 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_; only hvnlynab_rest_api_class_map uses hvnlynab_.
  • Overwriting arrays. Replacing an incoming meta_query or template mapping instead of merging into it, silently dropping other extensions’ contributions.

Need more help?

Can't find what you're looking for? Our team and community are here to help you ship faster.