# Frontend Page Hooks
These action hooks let you customize FluentCRM's frontend pages by injecting custom CSS, HTML, or scripts.
## Double Optin Confirmation Page
### `fluent_crm/confirmation_head`
This hook fires on the double optin confirmation page's `
`. Use it to add custom CSS or head attributes.
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber)
**Usage:**
```php
add_action('fluent_crm/confirmation_head', function($subscriber) {
?>
first_name;
});
```
**Source:** `app/Views/external/confirmation.php`
---
## Manage Subscription Page
### `fluent_crm/rendering_pref_form_shortcode`
This action fires before the manage subscription shortcode form is rendered. Use it to enqueue scripts or perform setup.
**Usage:**
```php
add_action('fluent_crm/rendering_pref_form_shortcode', function() {
// Enqueue custom scripts or perform setup
wp_enqueue_script('my-custom-script', '...');
});
```
**Source:** `app/Hooks/Handlers/PrefFormHandler.php`
---
### `fluent_crm/before_pref_form`
This hook fires inside the `[fluentcrm_pref]` preference form's `` tag, before the
response-message container. Use it to add content below the form.
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber) - the contact the form is rendered for
- `$fields` Array - the resolved form field definitions
**Usage:**
```php
add_action('fluent_crm/after_pref_form', function($subscriber, $fields) {
// Add your own code here
}, 10, 2);
```
**Source:** `app/Views/external/pref_form.php`
---
### `fluent_crm/manage_subscription_head`
This hook fires on the manage subscription page's ``. Use it to add custom CSS or head attributes.
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber)
**Usage:**
```php
add_action('fluent_crm/manage_subscription_head', function($subscriber) {
?>
first_name;
});
```
**Source:** `app/Views/external/manage_subscription.php`
---
### `fluent_crm/before_manage_sub_request_email`
This action fires before the manage subscription request email is sent (when a contact requests their subscription management URL).
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber)
- `$data` Array - email context data
**Usage:**
```php
add_action('fluent_crm/before_manage_sub_request_email', function($subscriber, $data) {
// Customize or log the manage subscription request
}, 10, 2);
```
**Source:** `app/Hooks/Handlers/ExternalPages.php`
---
### `fluent_crm/manage_sub_request_head`
The manage-subscription request page is a standalone page that renders when a visitor opens the
manage-subscription URL but cannot be identified (no valid secure hash). It asks for an email
address and emails the visitor a fresh subscription-management link. This hook fires in that page's
``, after `wp_head()` — it is the place to add custom CSS or head tags for the page.
::: tip
The setup action fired while this page renders is `fluent_crm/doing_unsubscribe_request_form` —
both the unsubscribe and manage-subscription request pages share that same hook.
:::
**Parameters**
_None._
**Usage:**
```php
add_action('fluent_crm/manage_sub_request_head', function() {
?>
`. Use it to add your own content below the form.
**Parameters**
_None._
**Usage:**
```php
add_action('fluent_crm/after_manage_sub_request_content', function() {
// Add your own code here
});
```
**Source:** `app/Views/external/manage_subscription_request_form.php`
---
### `fluent_crm/manage_sub_request_footer`
This hook fires at the end of the manage-subscription request page's ``, after `wp_footer()`.
Use it to add scripts or closing content.
**Parameters**
_None._
**Usage:**
```php
add_action('fluent_crm/manage_sub_request_footer', function() {
// Add your own code here
});
```
**Source:** `app/Views/external/manage_subscription_request_form.php`
---
## Unsubscribe Page
### `fluent_crm/doing_unsubscribe_request_form`
This action fires when the unsubscribe request form is being rendered. Use it to enqueue scripts or perform setup before the form loads.
**Usage:**
```php
add_action('fluent_crm/doing_unsubscribe_request_form', function() {
// Setup before unsubscribe form renders
});
```
**Source:** `app/Hooks/Handlers/ExternalPages.php`
---
### `fluent_crm/unsubscribe_request_head`
The unsubscribe request page is a standalone page that renders when a visitor opens the unsubscribe
URL but cannot be identified — there is no valid secure hash, or the campaign email in the URL does
not belong to the identified contact. It asks for an email address and emails the visitor a fresh
unsubscribe link. This hook fires in that page's ``, after `wp_head()` — it is the place to
add custom CSS or head tags for the page.
**Parameters**
_None._
**Usage:**
```php
add_action('fluent_crm/unsubscribe_request_head', function() {
?>
`. Use it to add your own content below the form.
**Parameters**
_None._
**Usage:**
```php
add_action('fluent_crm/after_unsubscribe_request_content', function() {
// Add your own code here
});
```
**Source:** `app/Views/external/unsubscribe_request_form.php`
---
### `fluent_crm/unsubscribe_request_footer`
This hook fires at the end of the unsubscribe request page's ``, after `wp_footer()`. Use it
to add scripts or closing content.
**Parameters**
_None._
**Usage:**
```php
add_action('fluent_crm/unsubscribe_request_footer', function() {
// Add your own code here
});
```
**Source:** `app/Views/external/unsubscribe_request_form.php`
---
### `fluent_crm/before_unsubscribe_request_email`
This action fires before the unsubscribe confirmation email is sent (when a contact requests to unsubscribe via the request form).
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber)
- `$data` Array - email context data
**Usage:**
```php
add_action('fluent_crm/before_unsubscribe_request_email', function($subscriber, $data) {
// Customize or log the unsubscribe request
}, 10, 2);
```
**Source:** `app/Hooks/Handlers/ExternalPages.php`
---
### `fluent_crm/unsubscribe_request_email_head`
This hook fires in the `` of the confirmation email that is sent after a visitor submits the
unsubscribe request form (the email that carries the actual unsubscribe link). Use it to add custom
CSS for the email — keep it email-client safe.
**Parameters**
_None._
**Usage:**
```php
add_action('fluent_crm/unsubscribe_request_email_head', function() {
?>
`. Use it to add custom CSS or head attributes.
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber)
- `$campaignEmail` [CampaignEmail Model](/database/models/campaign-email)
**Usage:**
```php
add_action('fluent_crm/unsubscribe_head', function($subscriber, $campaignEmail) {
?>
Custom message here';
}, 10, 2);
```
**Source:** `app/Views/external/unsubscribe.php`
---
### `fluent_crm/before_unsubscribe_form`
This hook fires before the unsubscribe form HTML (inside the content area). Use it to add custom HTML before the form.
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber)
- `$campaignEmail` [CampaignEmail Model](/database/models/campaign-email)
**Usage:**
```php
add_action('fluent_crm/before_unsubscribe_form', function($subscriber, $campaignEmail) {
// Add your own code here
}, 10, 2);
```
**Source:** `app/Views/external/unsubscribe.php`
---
### `fluent_crm/before_unsubscribe_submit`
This hook fires before the unsubscribe submit button. Use it to add custom HTML before the button.
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber)
- `$campaignEmail` [CampaignEmail Model](/database/models/campaign-email)
**Usage:**
```php
add_action('fluent_crm/before_unsubscribe_submit', function($subscriber, $campaignEmail) {
// Add your own code here
}, 10, 2);
```
**Source:** `app/Views/external/unsubscribe.php`
---
### `fluent_crm/after_unsubscribe_content`
This hook fires after the unsubscribe form content.
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber)
- `$campaignEmail` [CampaignEmail Model](/database/models/campaign-email)
**Usage:**
```php
add_action('fluent_crm/after_unsubscribe_content', function($subscriber, $campaignEmail) {
// Add your own code here
}, 10, 2);
```
**Source:** `app/Views/external/unsubscribe.php`
---
### `fluent_crm/unsubscribe_footer`
This hook fires on the unsubscribe page footer. Use it to add your own content.
**Parameters**
- `$subscriber` [Subscriber Model](/database/models/subscriber)
- `$campaignEmail` [CampaignEmail Model](/database/models/campaign-email)
**Usage:**
```php
add_action('fluent_crm/unsubscribe_footer', function($subscriber, $campaignEmail) {
if(!$subscriber) {
return;
}
echo 'Hello '.$subscriber->first_name;
}, 10, 2);
```
**Source:** `app/Views/external/unsubscribe.php`
---
## View On Browser Page
### `fluent_crm/view_on_browser_head`
This hook fires on the View On Browser page's ``. Use it to add custom CSS or head attributes.
**Parameters**
- `$campaignEmail` [CampaignEmail Model](/database/models/campaign-email)
**Usage:**
```php
add_action('fluent_crm/view_on_browser_head', function($campaignEmail) {
?>
FluentCRM Pro's experimental **Frontend Portal** (standalone mode) serves the CRM admin app at a
custom slug outside `wp-admin`, using a minimal "headless" HTML document. These hooks are that
document's extension points. Each receives a `$scope` string identifying which feature is rendering
the page — currently always `'crm_portal'`.
### `fluent_crm/headless/head_early`
This hook fires at the very top of the headless page's ``, before `wp_head()` runs (the portal
renders with WordPress head/footer loading enabled) and before the page's fallback meta tags. Use it
for tags that must precede everything else, such as early meta or preload hints.
**Parameters**
- `$scope` String - the feature rendering the page; currently always `crm_portal`
**Usage:**
```php
add_action('fluent_crm/headless/head_early', function($scope) {
if ($scope !== 'crm_portal') {
return;
}
echo '';
});
```
**Source:** `fluentcampaign-pro/app/Views/headless_page.php`
---
### `fluent_crm/headless/head`
This hook fires at the end of the headless page's ``, after `wp_head()` and the page's inline
styles. Use it to add custom CSS or head attributes.
**Parameters**
- `$scope` String - the feature rendering the page; currently always `crm_portal`
**Usage:**
```php
add_action('fluent_crm/headless/head', function($scope) {
?>
Welcome to the CRM portal';
}, 9);
```
**Source:** `fluentcampaign-pro/app/Views/headless_page.php` (rendered by `fluentcampaign-pro/app/Hooks/Handlers/FrontendPortalHandler.php`)
---
### `fluent_crm/headless/footer`
This hook fires at the end of the headless page's ``, after `wp_footer()`. Use it to add
scripts or closing content.
**Parameters**
- `$scope` String - the feature rendering the page; currently always `crm_portal`
**Usage:**
```php
add_action('fluent_crm/headless/footer', function($scope) {
// Add your own code here
});
```
**Source:** `fluentcampaign-pro/app/Views/headless_page.php`
---
## Public Request Dispatch Hooks
FluentCRM's public request router (`?fluentcrm=1`) exposes two dynamic action hooks as extension points. Both fire on **unauthenticated** requests.
### `fluentcrm_webhook_to_{handler}`
Fires for `?fluentcrm=1&route=webhook&handler={handler}`. Intended for inbound webhooks from external services (for example, the Pro SMS module listens on `fluentcrm_webhook_to_sms_webhook`).
**Parameters**
- `$data` (array) The full request data (`$_REQUEST`), **unsanitized**. Only the `handler` segment of the hook name is sanitized.
::: tip
If nothing hooks the handler, FluentCRM responds with a JSON `No Action found` payload and exits.
A listener that produces its own output should emit it and `exit()` before returning.
:::
**Source:** `app/Hooks/Handlers/ExternalPages.php`
### `fluent_crm/handle_frontend_for_{handler}`
Fires for `?fluentcrm=1&route=general&handler={handler}`. Intended for public frontend flows (for example, the abandoned-cart modules use it for cart-recovery links). Nothing fires when the `handler` parameter is empty.
**Parameters**
- `$data` (array) The full request data (`$_REQUEST`), **unsanitized**. Only the `handler` segment of the hook name is sanitized.
::: danger Listeners must authenticate their own requests
These hooks follow the same trust model as WordPress core's `wp_ajax_nopriv_{action}`: FluentCRM performs **no** authentication, capability check, or nonce verification before dispatching. Any visitor can trigger your listener with arbitrary request data.
If your listener changes state (writes to the database, sends email/SMS, modifies a contact), it **must** validate its own per-request secret before acting — for example a signed hash or an unguessable token issued when the URL was generated, as the built-in listeners do (the SMS webhook verifies a webhook hash; cart-recovery links require a matching per-cart `checkout_key`). Treat every field of `$data` as untrusted input.
:::
**Source:** `app/Hooks/Handlers/ExternalPages.php`