Hive Mail keeps its data in its own tables. Each is named {$wpdb->prefix}hm_<name>, for example wp_hm_contacts. All datetimes are stored in UTC. The schema version is in the option hmail_db_version; wp hivemail migrate creates or upgrades the tables (WP-CLI commands).
Warning: Read these tables if you need to, but do not write to them. The API and the PHP hooks apply the rules that tables cannot: consent and status transitions, suppression, the timeline, cached counts, locking and flow triggers. A direct
INSERTorUPDATEskips all of them, and a raw status change can email someone who never agreed. Use the REST API or the hooks.
How the audience tables relate #
hm_contactsis one row per person, unique byemail. A deleted contact hasdeleted_atset (the trash) until it is purged after 30 days.hm_contact_listjoins a contact to a list with its ownstatuson that list.hm_contact_tagjoins a contact to a tag.hm_contact_fieldsholds one row per contact and custom field;hm_fieldsdefines the field (slug,type,options). Short values are invalue, long text invalue_text.hm_segmentsstores only the rules (filters, JSON) and a cached count. Membership is computed by a query each time.hm_eventsis the contact timeline:type,contact_id, and the thing it is about (object_type,object_id).
<?php
global $wpdb;
// Read only: subscribed contacts on list 1.
$emails = $wpdb->get_col(
$wpdb->prepare(
"SELECT c.email FROM %i c JOIN %i cl ON cl.contact_id = c.id
WHERE cl.list_id = %d AND cl.status = 'subscribed' AND c.status = 'subscribed' AND c.deleted_at IS NULL",
$wpdb->prefix . 'hm_contacts',
$wpdb->prefix . 'hm_contact_list',
1
)
);
Audience #
| Table | Holds | Key columns |
|---|---|---|
hm_contacts |
People | id, email (unique), status, first_name, last_name, phone, country, company, source, wp_user_id, token, score, consent_source, consent_at, subscribed_at, deleted_at |
hm_fields |
Custom field definitions | id, slug (unique), label, type, options, sort_order |
hm_contact_fields |
Custom field values | contact_id, field_id (together the key), value, value_text |
hm_lists |
Lists | id, name, slug (unique), description, is_public |
hm_contact_list |
List membership | contact_id, list_id (key), status, source, added_at |
hm_tags |
Tags | id, name, slug (unique), color |
hm_contact_tag |
Tag assignment | contact_id, tag_id (key), added_at |
hm_segments |
Saved filters | id, name, filters (JSON rules), cached_count, cached_at |
hm_suppressions |
The do-not-email list | id, email (unique), reason (bounce, complaint, manual), provider, email_hash (unique). When a contact who unsubscribed or bounced is deleted for good, a row whose email is removed: plus a hash (reason bounce, complaint or unsubscribed) stands in for the address |
hm_forms |
Hive Mail signup forms | id, title, status (active, draft), settings (JSON), submissions, confirmed, version |
Campaigns and sending #
| Table | Holds | Key columns |
|---|---|---|
hm_templates |
Email designs | id, name, editor (blocks, classic, text, html), design (JSON), html, version |
hm_template_revisions |
A template's history | id, template_id, version, design, reason, user_id |
hm_campaigns |
Campaigns | id, name, type, status, subject, template_id, audience (JSON), design, html, stats (JSON), cursor_id, scheduled_at, started_at, finished_at |
hm_messages |
The send queue and the send log, one row per email | id, campaign_id, run_id, node_id, contact_id, category (marketing, transactional), status, scheduled_at, attempts, sent_at, provider_message_id, opened_at, clicked_at, error. Unique on (campaign_id, contact_id). The body of a campaign or flow email is not stored; payload (JSON) holds the content of one-off emails such as a confirmation request |
hm_links |
Tracked link destinations | id, short (unique), campaign_id, url |
hm_attributions |
Orders credited to an email | source, order_id (unique together), contact_id, message_id, campaign_id, automation_id, amount, currency |
Automation #
| Table | Holds | Key columns |
|---|---|---|
hm_automations |
Flows | id, name, status, trigger_key, graph (JSON), version, settings (JSON), stats |
hm_automation_versions |
Every graph version a flow was switched on with | automation_id, version (key), graph |
hm_automation_runs |
One row per person in a flow | id, automation_id, contact_id, graph_version, enrol_key (unique), current_node, status, next_run_at, context |
hm_automation_log |
One row per step taken | id, run_id, node_id, event, message, created_at |
Shops #
| Table | Holds | Key columns |
|---|---|---|
hm_commerce_index |
One row per customer and shop, recomputed from the orders | source (woo, edd), email (unique together), contact_id, orders_count, total_spent, currency, first_order_at, last_order_at, product_ids, category_ids |
hm_carts |
WooCommerce carts that have an email | id, cart_key, email, contact_id, status (open, abandoned, recovered, ordered), items, total, restore_key, order_id |
Visitor tracking #
| Table | Holds | Key columns |
|---|---|---|
hm_visitors |
Anonymous or identified browsers | id, uid (unique), anon, contact_id, first_seen, last_seen, first_source, first_medium, consent |
hm_sessions |
Visits | id, visitor_id, started_at, source, medium, campaign, referrer_host, hits, pageviews |
hm_hits |
Page views and events | id, ts, visitor_id, session_id, page_id, type, value, meta |
hm_pages |
Page addresses | id, path (unique), title |
hm_stats_daily |
Daily report totals | kind, day, ref, k1 to k4, views, visitors, sessions, conversions, amount |
hm_score_ledger |
Engagement points already given | contact_id, kind, ref (key), day |
hm_rate |
Rate-limit counters | bucket, minute (key), n |
System #
| Table | Holds | Key columns |
|---|---|---|
hm_events |
The contact timeline and email events | id, ts, type, contact_id, visitor_id, object_type, object_id, value |
hm_jobs |
Background jobs (imports, exports, enrolments, backfills) | id, type, status, payload, total, processed, result, error |
hm_logs |
The plugin's log | id, level, channel, message, context, created_at |
The AI assistant keeps its own tables, hm_ai_conversations, hm_ai_messages, hm_ai_memory and hm_ai_usage. They are internal: use the assistant's screens, not queries.
Uninstalling with Delete all data removes every table above (Uninstalling). What each one stores about people is on What data Hive Mail stores.