---
title: "Hooks: actions"
description: Every Hive Mail action hook grouped by area, with its arguments and when it fires, for code that reacts to contacts, campaigns and flows.
url: "https://hiveplugins.com/docs/hive-mail-hooks-actions/"
updated: "2026-10-28"
category: Hive Mail
---

# Hooks: actions

An action runs your code when something happens. Add it with `add_action( $hook, $callback, $priority, $accepted_args )` and pass the number of arguments you use as the fourth value. Every hook below exists in the plugin code.

```
<?php
// Write a log line when a person confirms their subscription.
add_action(
    'hivemail_contact_confirmed',
    function ( $contact_id, $list_id ) {
        error_log( "Contact $contact_id confirmed list $list_id" );
    },
    10,
    2
);
```

## Contacts

| Hook | Arguments | Fires |
| --- | --- | --- |
| `hivemail_contact_created` | `$contact`, `$ctx` | After a contact is created (`$ctx` is the change context, with `source`) |
| `hivemail_contact_updated` | `$after`, `$before`, `$changed` | After contact fields change (`$changed`: column => new value) |
| `hivemail_contact_fields_changed` | `$contact`, `$changed_fields` | After custom field values change (`$changed_fields`: slugs) |
| `hivemail_contact_status_changed` | `$contact`, `$from`, `$to`, `$ctx` | After the status changes (also when a bounced contact is reactivated) |
| `hivemail_contact_trashed` | `$id` | After a contact moves to the trash |
| `hivemail_contact_restored` | `$id` | After a contact is restored from the trash |
| `hivemail_contact_deleted` | `$id`, `$email_hash` | After a contact is deleted for good (only the `md5` of the email is passed) |
| `hivemail_contact_added_to_list` | `$contact_id`, `$list_id`, `$source` | After a contact joins a list |
| `hivemail_contact_removed_from_list` | `$contact_id`, `$list_id` | After a contact leaves a list |
| `hivemail_contact_list_status_changed` | `$contact_id`, `$list_id`, `$status` | After a contact's status on one list changes |
| `hivemail_contact_tagged` | `$contact_id`, `$tag_id` | After a contact gets a tag |
| `hivemail_contact_untagged` | `$contact_id`, `$tag_id` | After a tag is removed from a contact |
| `hivemail_contact_confirmed` | `$contact_id`, `$list_id` | When a person confirms by double opt-in (`$list_id` is 0 if none) |
| `hivemail_unsubscribed` | `$contact`, `$message_id`, `$reason` | When a person unsubscribes through the link in an email |
| `hivemail_bounce_processed` | `$type`, `$email`, `$contact_id`, `$provider` | After a bounce or complaint is applied (`$type`: `bounce` or `complaint`; `$contact_id` is 0 if the address is not a contact) |

## Lists, tags, custom fields and segments

| Hook | Arguments | Fires |
| --- | --- | --- |
| `hivemail_list_created` | `$list` | After a list is created |
| `hivemail_list_updated` | `$after`, `$before` | After a list is saved |
| `hivemail_list_deleted` | `$id` | After a list is deleted |
| `hivemail_tag_created` | `$tag` | After a tag is created |
| `hivemail_tag_updated` | `$after`, `$before` | After a tag is saved |
| `hivemail_tag_deleted` | `$id` | After a tag is deleted |
| `hivemail_field_created` | `$field` | After a custom field is created |
| `hivemail_field_updated` | `$after`, `$before` | After a custom field is saved |
| `hivemail_field_deleted` | `$id`, `$field` | After a custom field is deleted |
| `hivemail_segment_created` | `$segment` | After a segment is created |
| `hivemail_segment_updated` | `$after`, `$before` | After a segment is saved |
| `hivemail_segment_deleted` | `$id` | After a segment is deleted |

## Campaigns, templates and email

| Hook | Arguments | Fires |
| --- | --- | --- |
| `hivemail_campaign_created` | `$campaign` | After a campaign is created |
| `hivemail_campaign_updated` | `$after`, `$before` | After a draft campaign is changed |
| `hivemail_campaign_deleted` | `$id` | After a campaign is deleted |
| `hivemail_campaign_started` | `$campaign` | When a campaign starts sending |
| `hivemail_campaign_sent` | `$campaign` | When every message of a campaign is sent, skipped or failed for good |
| `hivemail_campaign_status_changed` | `$campaign`, `$from`, `$to` | When a campaign moves to another state |
| `hivemail_template_created` | `$template` | After a template is created |
| `hivemail_template_updated` | `$after`, `$before` | After a template is saved |
| `hivemail_template_deleted` | `$id` | After a template is deleted |
| `hivemail_brand_kit_updated` | `$kit` | After the brand kit is saved |
| `hivemail_email_sent` | `$message`, `$email` | After an email is handed to the delivery method successfully |
| `hivemail_email_opened` | `$message` | When an open is counted |
| `hivemail_email_clicked` | `$message`, `$link_id` | When a click is counted |

## Flows and recipes

| Hook | Arguments | Fires |
| --- | --- | --- |
| `hivemail_flow_created` | `$id` | After a flow is created |
| `hivemail_flow_updated` | `$id`, `$changed_keys` | After a flow is saved (`$changed_keys`: the names of the saved parts) |
| `hivemail_flow_deleted` | `$id` | After a flow is deleted |
| `hivemail_flow_activated` | `$id` | After a flow is switched on |
| `hivemail_flow_paused` | `$id` | After a flow is paused |
| `hivemail_flow_stopped` | `$id`, `$ended` | After a flow is stopped (`$ended`: how many runs were stopped) |
| `hivemail_recipe_installed` | `$recipe_id`, `$flow_id` | After "Use this automation" makes a flow |

## Forms

| Hook | Arguments | Fires |
| --- | --- | --- |
| `hivemail_form_created` | `$form` | After a signup form is created |
| `hivemail_form_updated` | `$after`, `$before` | After a signup form is saved |
| `hivemail_form_deleted` | `$id` | After a signup form is deleted |
| `hivemail_signup_form_submitted` | `$form`, `$contact_id`, `$outcome` | After a real person sends a signup form (not a robot, not over the limit). `$outcome`: `created_subscribed`, `created_pending`, `existing_subscribed`, `pending_resent`, `rejoin_asked`, `transactional_subscribed`, `transactional_pending`, `ignored`, `suppressed` or `trashed` |
| `hivemail_form_connected` | `$flow_id`, `$marker` | After a form of another plugin is connected (the flow exists and is still off). `$marker` has `key`, `form_id`, `form_title`, `use_case` |
| `hivemail_form_disconnected` | `$flow_id` | After a connected form is disconnected |
| `hivemail_form_submitted` | `$source`, `$form_id`, `$fields` | After a form of a supported form plugin is submitted (`$source` is the plugin's key, for example `fluentforms`) |

## Shops

| Hook | Arguments | Fires |
| --- | --- | --- |
| `hivemail_commerce_order_event` | `$event`, `$order`, `$raw`, `$source` | When an order event is handled. `$event`: `placed`, `paid`, `status:{status}` or `refunded`. `$raw` is the shop's own order object. `$source`: `woo` or `edd` |
| `hivemail_commerce_indexed` | `$source`, `$email`, `$row` | After a customer's summary row is saved (`$row` is `null` when it was removed) |
| `hivemail_cart_abandoned` | `$cart`, `$contact_id` | When a WooCommerce cart is marked abandoned (`$contact_id` is 0 if nobody may be emailed) |
| `hivemail_cart_recovered` | `$cart`, `$order_id` | When an abandoned cart is recovered by an order |
| `hivemail_cart_restored` | `$cart`, `$wc_cart` | When a restore link refills a cart (apply a coupon here) |

## Visitors (marketing intelligence)

These run when the visitor tracker records a batch, and only if something listens. A listener that throws is logged and skipped. Treat every value that came from the browser (paths, titles, sources, props) as untrusted text.

| Hook | Arguments | Fires |
| --- | --- | --- |
| `hivemail_visitor_identified` | `$visitor_id`, `$contact_id`, `$signal`, `$previous` | When a visitor is recognised as a contact. `$signal`: `click`, `form`, `login` or `order`. `$previous` is the contact it was linked to before (0 = none) |
| `hivemail_session_started` | `$session` | For each new visit in a stored batch (`id`, `source`, `medium`, `campaign`, `referrer_host`, `landing`, `started_at`, `hits`, `is_new`) |
| `hivemail_hit_recorded` | `$hit`, `$visitor`, `$session` | For each stored hit. `$hit` has `id`, `ts`, `meta`, `page`. `$visitor` has `id`, `contact_id` |
| `hivemail_intel_goal_reached` | `$goal`, `$visitor`, `$session`, `$value` | When a visit reaches a conversion goal, once per goal and visit. `$goal` has `id`, `name`, `kind` |

## System and extension points

| Hook | Arguments | Fires |
| --- | --- | --- |
| `hivemail_loaded` | `$plugin` | When Hive Mail has finished booting (on `plugins_loaded`) |
| `hivemail_tick` | none | On every scheduler tick, about once a minute. Run short work here |
| `hivemail_daily` | none | Once a day, for clean-up work |
| `hivemail_settings_updated` | `$group`, `$values` | After a Settings tab is saved (`$group`: `general`, `sending`, `tracking`, ...) |
| `hivemail_job_completed` | `$job` | When a background job (an import, an export) completes |
| `hivemail_admin_scripts` | `$handle` | After Hive Mail enqueues its admin app, on its screens only. Enqueue your script here with `hivemail-admin` as a dependency |
| `hivemail_register_triggers` | `$registry` | The first time the flow trigger registry is read: register your trigger |
| `hivemail_register_nodes` | `$registry` | Flow steps (nodes) |
| `hivemail_register_segment_conditions` | `$registry` | Segment rule types |
| `hivemail_register_integrations` | `$registry` | Plugin integrations (shops, form plugins) |
| `hivemail_register_channels` | `$registry` | Delivery channels |
| `hivemail_register_transports` | `$registry` | Email transports |
| `hivemail_register_blocks` | `$registry` | Visual builder blocks |
| `hivemail_register_bounce_providers` | `$registry` | Bounce webhook adapters (`generic`, `mailgun`, `sendgrid` are built in) |
| `hivemail_roles_changed` | `$roles` | After the Roles & Permissions table is saved (`$roles`: the role slugs that changed) |
| `hivemail_user_permissions_changed` | `$user_id`, `$capabilities` | After permissions for one person are saved |

Each registry also has a filter with the same name without `register_`, for example `hivemail_triggers`. See [Hooks: filters](/docs/hive-mail-hooks-filters/).

> **Warning:** `hivemail_contact_deleted` passes only the `md5` of the email. A deleted contact's address is not kept. Keep callbacks on `hivemail_tick`, `hivemail_email_sent` and `hivemail_hit_recorded` fast: they run in the sending and tracking paths.

Next: [Hooks: filters](/docs/hive-mail-hooks-filters/), [Extending Hive Mail](/docs/hive-mail-extending-hive-mail/).
