# Database tables

Look up every column, type, default and index of the eight Link Manager Pro tables, and see what writes to each one.
Page: https://docs.linkmanagerpro.com/developers/data/tables
Use when someone asks: link manager pro database tables; link_manager_pro_links table; link_manager_pro_clicks columns; link manager pro database schema
Intent: Reference

Link Manager Pro creates eight tables named `{$wpdb->prefix}link_manager_pro_{name}`. Migrations in `database/Migrations` build them with WordPress `dbDelta()`, and every index is part of its `CREATE TABLE`.

Migrations run on activation and, when the stored plugin version trails the running one, when the plugin loads. The option `link_manager_pro_version` records the version the tables were last migrated for. For where everything else lives, see [Where Link Manager Pro stores data](https://docs.linkmanagerpro.com/developers/data/).

The schema builder maps the migration methods to these MySQL types:

| Builder method | Column type |
|---|---|
| `id()` | `bigint unsigned NOT NULL AUTO_INCREMENT`, the primary key |
| `unsignedBigInteger()` | `bigint unsigned` |
| `integer()` | `int` |
| `boolean()` | `tinyint(1)` |
| `string('x', 64)` | `varchar(64)` |
| `text()` | `text` |
| `dateTime()` | `datetime` |
| `timestamps()` | `created_at` and `updated_at`, both `timestamp NULL` |
| `enum()` | `enum(...)` |

A column is `NOT NULL` unless the table below says `NULL`. Fields stored as JSON, such as `keywords`, are text columns. The models decode them to arrays.

Don't write to these tables directly. Use the [REST API](https://docs.linkmanagerpro.com/developers/rest-api/) or the [hooks](https://docs.linkmanagerpro.com/developers/hooks/) so caches and counters stay correct.

## The tables

### `{prefix}link_manager_pro_links`

One row per short link.

| Column | Type | Comment |
|---|---|---|
| `id` | `bigint unsigned NOT NULL AUTO_INCREMENT` | Primary key |
| `slug` | `varchar(64) NOT NULL` | The address part after the short link prefix |
| `target_url` | `text NOT NULL` | The destination |
| `redirect_type` | `int NOT NULL DEFAULT 302` | The HTTP status of the redirect |
| `source` | `varchar(16) NOT NULL DEFAULT 'manual'` | How the link was created: `manual`, `scan`, `editor`, `affiliate`, `health`, `ai`, `automatorwp`, `imported`, `prettylinks`, `betterlinks`, `thirstyaff` or `redirect` (a redirect's hidden link) |
| `url_hash` | `varchar(32) NOT NULL` | A 32-character hash of the destination |
| `keywords` | `text NULL` | JSON array of auto-link keywords |
| `enable_autolink` | `tinyint(1) NOT NULL DEFAULT 0` | Whether auto-linking uses this link |
| `autolink_priority` | `int NOT NULL DEFAULT 10` | Auto-link order |
| `link_attributes` | `text NULL` | JSON object of link attributes |
| `click_count` | `int NOT NULL DEFAULT 0` | Total clicks |
| `forward_params` | `tinyint(1) NOT NULL DEFAULT 0` | Whether visitor query parameters pass to the destination |
| `link_password` | `varchar(255) NULL` | The link's password, stored as a WordPress password hash |
| `utm_params` | `text NULL` | JSON object of this link's UTM values |
| `active_from` | `datetime NULL` | The link starts working at this time |
| `expires_at` | `datetime NULL` | The link stops working at this time |
| `created_at` | `timestamp NULL` | |
| `updated_at` | `timestamp NULL` | |

**Indexes:** `slug`, `url_hash`, `enable_autolink`, `click_count`. All are plain keys, not unique keys.

**Written by:** creating, editing and deleting short links, imports from other plugins, backup restore, and Simple Redirects, which keep a short link row with `source` set to `redirect`.

### `{prefix}link_manager_pro_clicks`

One row per recorded click.

| Column | Type | Comment |
|---|---|---|
| `id` | `bigint unsigned NOT NULL AUTO_INCREMENT` | Primary key |
| `click_type` | `varchar(20) NOT NULL DEFAULT 'masked'` | `masked` for a short link click, `internal` for a tracked internal link click |
| `link_id` | `bigint unsigned NULL` | The short link. Empty for internal clicks |
| `post_id` | `bigint unsigned NULL` | The post the click came from, for internal clicks |
| `user_id` | `bigint unsigned NULL` | The logged-in WordPress user, if any |
| `cookie_id` | `varchar(64) NULL` | The visitor's `link_manager_pro_id` cookie value |
| `is_unique` | `tinyint(1) NOT NULL DEFAULT 1` | Whether the click counted as unique |
| `ip_address` | `varchar(45) NULL` | The visitor IP, anonymized if that setting is on |
| `user_agent` | `text NULL` | The browser's user agent |
| `country_code` | `varchar(2) NULL` | Two-letter country code |
| `referrer_url` | `varchar(2048) NULL` | The Referer URL |
| `referrer_domain` | `varchar(255) NULL` | The Referer host |
| `matched_rule_id` | `bigint unsigned NULL` | The routing rule that chose the destination. Saving a link's rules recreates them with new IDs, so an older click can point to a rule that no longer exists |
| `target_url` | `varchar(2048) NULL` | The destination, for internal clicks |
| `target_url_hash` | `varchar(64) NULL` | SHA-256 of the destination, for internal clicks |
| `anchor_text` | `varchar(500) NULL` | The link text, for internal clicks |
| `clicked_at` | `datetime NOT NULL` | When the click happened, in UTC |
| `created_at` | `timestamp NULL` | |
| `updated_at` | `timestamp NULL` | |

**Indexes:** `link_id`; `clicked_at`; `post_id`; `link_id, cookie_id`; `link_id, ip_address`; `click_type, post_id, target_url_hash`, named `lmp_clicks_internal_dedupe_idx`.

**Written by:** short link redirects and the internal click tracker. Imports from other plugins and backup restore also write click rows. Inbound `utm_*` values for a click go to the meta table, not to this one.

### `{prefix}link_manager_pro_affiliates`

One row per affiliate partner.

| Column | Type | Comment |
|---|---|---|
| `id` | `bigint unsigned NOT NULL AUTO_INCREMENT` | Primary key |
| `site_name` | `varchar(100) NOT NULL` | The partner's display name |
| `domain` | `varchar(100) NOT NULL` | The partner's domain |
| `affiliate_link` | `text NOT NULL` | The affiliate link |
| `keywords` | `text NULL` | JSON array of auto-link keywords |
| `enable_autolink` | `tinyint(1) NOT NULL DEFAULT 0` | Whether auto-linking uses this partner |
| `autolink_priority` | `int NOT NULL DEFAULT 10` | Auto-link order |
| `attributes` | `text NULL` | Link attributes |
| `utm_params` | `text NULL` | UTM values |
| `redirect_type` | `int NOT NULL DEFAULT 301` | The HTTP status of the redirect |
| `click_count` | `int NOT NULL DEFAULT 0` | Total clicks |
| `created_at` | `timestamp NULL` | |
| `updated_at` | `timestamp NULL` | |

**Indexes:** `domain`, `enable_autolink`, `click_count`.

**Written by:** the affiliates screen and its REST routes, and backup restore. The partner's **Auto-link only on** choice is stored in the meta table with `entity_type` `affiliate`.

### `{prefix}link_manager_pro_link_health`

One row per broken link found by Link Health.

| Column | Type | Comment |
|---|---|---|
| `id` | `bigint unsigned NOT NULL AUTO_INCREMENT` | Primary key |
| `url` | `text NOT NULL` | The broken URL |
| `source_post_id` | `bigint unsigned NULL` | The post that links to it |
| `health_type` | `enum('content','traffic') NOT NULL` | `content` for a link found in a post, `traffic` for a visitor 404 |
| `status_code` | `int NULL` | The HTTP status found |
| `referrer` | `text NULL` | The referring page |
| `hits` | `int NOT NULL DEFAULT 1` | How often it was detected |
| `last_detected` | `datetime NOT NULL` | When it was last detected, in UTC |
| `status` | `enum('active','ignored','fixed') NOT NULL DEFAULT 'active'` | Where the issue stands |
| `created_at` | `timestamp NULL` | |
| `updated_at` | `timestamp NULL` | |

**Indexes:** `health_type, status`; `status`; `source_post_id`.

**Written by:** Link Health scans, visitor 404 logging and backup restore. The daily purge removes old `traffic` rows when that retention setting is on.

### `{prefix}link_manager_pro_redirects`

One row per Simple Redirect. The destination lives on a short link row that the redirect points to.

| Column | Type | Comment |
|---|---|---|
| `id` | `bigint unsigned NOT NULL AUTO_INCREMENT` | Primary key |
| `source_url` | `text NOT NULL` | The old address |
| `source_url_hash` | `varchar(32) NOT NULL` | A 32-character hash of the old address |
| `target_link_id` | `bigint unsigned NOT NULL` | The `links` row that holds the destination |
| `status` | `varchar(50) NOT NULL DEFAULT 'active'` | The redirect's state |
| `created_at` | `timestamp NULL` | |
| `updated_at` | `timestamp NULL` | |

**Indexes:** `source_url_hash`, `target_link_id`, `status`.

**Written by:** creating and editing Simple Redirects, and backup restore.

### `{prefix}link_manager_pro_link_rules`

One row per routing rule of a short link.

| Column | Type | Comment |
|---|---|---|
| `id` | `bigint unsigned NOT NULL AUTO_INCREMENT` | Primary key |
| `link_id` | `bigint unsigned NOT NULL` | The short link the rule belongs to |
| `name` | `varchar(120) NULL` | The rule's name |
| `is_active` | `tinyint(1) NOT NULL DEFAULT 1` | Whether the rule is on |
| `priority` | `int NOT NULL DEFAULT 100` | Evaluation order |
| `match_mode` | `varchar(10) NOT NULL DEFAULT 'all'` | `all` or `any`: whether every condition or one condition must match |
| `target_url` | `text NOT NULL` | The destination when the rule matches |
| `redirect_type` | `int NOT NULL DEFAULT 302` | The HTTP status of the redirect |
| `start_at` | `datetime NULL` | The rule starts at this time |
| `end_at` | `datetime NULL` | The rule ends at this time |
| `conditions` | `text NULL` | JSON list of conditions |
| `created_at` | `timestamp NULL` | |
| `updated_at` | `timestamp NULL` | |

**Indexes:** `link_id, is_active, priority`.

**Written by:** the routing rules REST routes and screen, and backup restore.

### `{prefix}link_manager_pro_link_audit`

One row per post that Link Audit has scanned.

| Column | Type | Comment |
|---|---|---|
| `id` | `bigint unsigned NOT NULL AUTO_INCREMENT` | Primary key |
| `post_id` | `bigint unsigned NOT NULL` | The scanned post |
| `post_type` | `varchar(20) NOT NULL DEFAULT 'post'` | The post's type |
| `post_title` | `varchar(255) NOT NULL DEFAULT ''` | The post's title |
| `inbound_internal` | `int NOT NULL DEFAULT 0` | Internal links pointing to the post |
| `outbound_internal` | `int NOT NULL DEFAULT 0` | Internal links the post contains |
| `outbound_external` | `int NOT NULL DEFAULT 0` | External links the post contains |
| `status` | `varchar(20) NOT NULL DEFAULT 'healthy'` | `healthy`, `weak` or `orphaned` |
| `scanned_at` | `datetime NULL` | When the post was last scanned |
| `created_at` | `timestamp NULL` | |
| `updated_at` | `timestamp NULL` | |

**Indexes:** `post_id`.

**Written by:** Link Audit scans and the sync that runs when a post or a short link changes. A backup doesn't include this table.

### `{prefix}link_manager_pro_meta`

A key-value table for everything that doesn't need its own table. A row belongs to an entity, named by `entity_type` and `entity_id`.

| Column | Type | Comment |
|---|---|---|
| `id` | `bigint unsigned NOT NULL AUTO_INCREMENT` | Primary key |
| `entity_type` | `varchar(100) NOT NULL` | What kind of record the row belongs to (see below) |
| `entity_id` | `bigint unsigned NOT NULL` | The record's ID, or `0` for site-wide rows |
| `meta_key` | `varchar(255) NOT NULL` | The key |
| `meta_value` | `text NULL` | The value. Arrays are stored as JSON |
| `created_at` | `timestamp NULL` | |
| `updated_at` | `timestamp NULL` | |

**Indexes:** `entity_type, entity_id`; `entity_type(50), meta_key(140)`.

**Written by:** the settings class, short link addresses, campaigns, UTM templates, webhooks, click tracking, Link Coverage, imports from other plugins and the daily maintenance.

The entity types:

| `entity_type` | `entity_id` | `meta_key` | What `meta_value` holds |
|---|---|---|---|
| `setting` | `0` | The settings group | The group's values as JSON |
| `link` | The short link ID | `address:` plus an address, and import markers | Other addresses of the link, a Simple Redirect's case setting (`redirect_any_case`) and import bookkeeping |
| `click` | The click ID | `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content` | The inbound UTM value, up to 191 characters |
| `affiliate` | The affiliate ID | `autolink_terms` | Category and tag IDs for **Auto-link only on** |
| `campaign` | `0` | The campaign slug | The Managed campaign's settings |
| `utm_template` | `0` | The template name | The template's UTM values |
| `webhook` | `0` | `config` | The webhook's settings, with its secret encrypted |
| `webhook_delivery` | The webhook ID | The delivery status | One queued or finished delivery as JSON |
| `coverage_score` | `0` | `week_{year}_{week}` | The weekly Link Coverage score, grade and counts |
| `deleted_slug` | `0` | The slug | Marks a slug that a short link was deleted or renamed away from, so it isn't recreated. Cleared when a link takes the slug again |

## Restore safety copies

Before a backup restore replaces an item, it copies the current rows into tables named `{table}__lmpsnap`. For example, restoring links copies `{prefix}link_manager_pro_links` into `{prefix}link_manager_pro_links__lmpsnap`.

Some copies have other names:

- Restoring settings, clicks or links also copies the matching meta rows into `{prefix}link_manager_pro_meta__lmpsnap_setting`, `__lmpsnap_click` or `__lmpsnap_link`.
- Restoring categories or tags copies the WordPress `terms`, `term_taxonomy`, `term_relationships` and `termmeta` rows into tables ending `__lmpsnap_categories` or `__lmpsnap_tags`.
- Restoring posts copies the posts into `{prefix}posts__lmpsnap`.

When the restore succeeds, it drops the copies of each restored item. When it fails, the plugin puts the rows back from the copies and drops them. A copy stays only when the rollback itself can't finish, and the restore's error message names the tables kept. Uninstall removes any `__lmpsnap` copy of the eight plugin tables.

## Related

- [Where Link Manager Pro stores data](https://docs.linkmanagerpro.com/developers/data/)
- [Backup file format](https://docs.linkmanagerpro.com/developers/data/backup-format)
- [REST API](https://docs.linkmanagerpro.com/developers/rest-api/)
- [WP-CLI for developers](https://docs.linkmanagerpro.com/developers/cli)
