# Action hooks

Link Manager Pro has seven action hooks. Four report link events, one fires after the plugin loads, one after settings are saved, and one asks page caches to purge.
Page: https://docs.linkmanagerpro.com/developers/hooks/actions
Use when someone asks: link manager pro action hooks; link_manager_pro_link_clicked; run code when a short link is clicked; link_manager_pro_settings_updated
Intent: Reference

Hook them with `add_action()`: the four link events, `link_manager_pro_booted`, `link_manager_pro_settings_updated` and `link_manager_pro_cache_should_purge`.

Add the code to your own plugin or a must-use plugin. See [Use a hook](https://docs.linkmanagerpro.com/developers/hooks/#use-a-hook) for priority and argument rules.

## Events

Each event passes one array. The `event` key holds the hook name without the `link_manager_pro_` prefix, so `link_manager_pro_link_clicked` sends `event` set to `link_clicked`. All values are plain strings and numbers, never objects.

The same four events feed webhooks and AutomatorWP. See [Events, webhooks and AutomatorWP](https://docs.linkmanagerpro.com/developers/hooks/events).

A listener that throws an error never breaks the plugin. An event fired again from inside its own listener is skipped, so a listener cannot loop.

### `link_manager_pro_link_clicked`

Runs your code each time a visitor's click on a short link is counted.

**When it runs:** after a short link redirect adds one to the link's click count. It does not run for rate-limited requests, or for clicks the plugin filters out (bots and blocked IP addresses). It runs whether or not click rows are logged. It also runs when a redirect is served, with the slug of the redirect's hidden link.

**Parameters:**
- `$payload` (array): the event.
  - `event` (string): `link_clicked`.
  - `link_id` (int): the link ID.
  - `slug` (string): the link slug.
  - `target_url` (string): the link's destination URL.
  - `click_count` (int): the link's click count after this click.
  - `country_code` (string): the visitor's country code, or an empty string when unknown.
  - `referrer_domain` (string): the referrer host in lowercase without `www.`, or an empty string.

**Source:** `app/Http/Controllers/RedirectController.php`, `app/Support/Events.php`

**Usage:**

```php
add_action( 'link_manager_pro_link_clicked', 'myprefix_log_click' );
function myprefix_log_click( array $payload ): void {
	global $wpdb;
	$wpdb->insert(
		$wpdb->prefix . 'myprefix_clicks',
		array(
			'link_id'    => $payload['link_id'],
			'country'    => $payload['country_code'],
			'created_at' => current_time( 'mysql', true ),
		)
	);
}
```

### `link_manager_pro_link_created`

Runs your code when a new short link is created.

**When it runs:** after the link is saved, including its categories, tags and rules. It does not run for links added by the Pretty Links, BetterLinks and ThirstyAffiliates importers, for links put back by a backup restore, or for the hidden links behind redirects and Link Health fixes.

**Parameters:**
- `$payload` (array): the event.
  - `event` (string): `link_created`.
  - `link_id` (int): the new link ID.
  - `slug` (string): the link slug.
  - `target_url` (string): the destination URL.
  - `redirect_type` (int): the link's redirect status code: `301`, `302` or `307`.
  - `created_by` (int): the ID of the user who created the link, or 0 when no user is signed in.

**Source:** `app/Services/LinkService.php`, `app/Support/Events.php`

**Usage:**

```php
add_action( 'link_manager_pro_link_created', 'myprefix_notify_new_link' );
function myprefix_notify_new_link( array $payload ): void {
	wp_remote_post(
		'https://example.com/notify',
		array(
			'headers' => array( 'Content-Type' => 'application/json' ),
			'body'    => wp_json_encode(
				array( 'text' => 'New short link: ' . $payload['slug'] . ' to ' . $payload['target_url'] )
			),
		)
	);
}
```

### `link_manager_pro_broken_link_found`

Runs your code when the link checker finds a broken link in your content for the first time.

**When it runs:** when a broken outbound link is first recorded for a post. It does not run again when the same link in the same post is seen broken again. It does not run for visitor 404 errors.

**Parameters:**
- `$payload` (array): the event.
  - `event` (string): `broken_link_found`.
  - `url` (string): the broken URL.
  - `source_post_id` (int): the post that contains the link, or 0.
  - `status_code` (int): the HTTP status code, or 0 when there is none.
  - `health_type` (string): always `content`.
  - `detected_at` (string): the detection time in UTC, as `Y-m-d H:i:s`.

**Source:** `app/Services/Monitoring/LinkHealthService.php`, `app/Support/Events.php`

**Usage:**

```php
add_action( 'link_manager_pro_broken_link_found', 'myprefix_mail_broken_link' );
function myprefix_mail_broken_link( array $payload ): void {
	wp_mail(
		get_option( 'admin_email' ),
		'Broken link found',
		$payload['url'] . ' (status ' . $payload['status_code'] . ') in post ' . $payload['source_post_id']
	);
}
```

### `link_manager_pro_broken_link_fixed`

Runs your code when a broken link is marked fixed or gets a redirect.

**When it runs:** when an issue changes to Fixed (a status change to anything else does not count), or when a redirect is created for it. It does not run again for an issue that is already fixed.

**Parameters:**
- `$payload` (array): the event.
  - `event` (string): `broken_link_fixed`.
  - `url` (string): the URL that was broken.
  - `source_post_id` (int): the post that contained it, or 0.
  - `health_type` (string): `content` or `traffic`.
  - `resolution` (string): `marked_fixed` or `redirect_created`.

**Source:** `app/Http/Controllers/LinkHealthController.php`, `app/Support/Events.php`

**Usage:**

```php
add_action( 'link_manager_pro_broken_link_fixed', 'myprefix_log_fix' );
function myprefix_log_fix( array $payload ): void {
	error_log( 'Fixed ' . $payload['url'] . ' by ' . $payload['resolution'] );
}
```

## Lifecycle

### `link_manager_pro_booted`

Runs your code once the plugin has finished loading.

**When it runs:** while the plugin file loads, after all providers are registered and booted. A callback in a normal plugin that loads later is too late, so register it in a must-use plugin.

**Parameters:**
- `$app` (`LinkManagerPro\Framework\Foundation\Application`): the plugin application container.

**Source:** `bootstrap/app.php`

**Usage:**

```php
// wp-content/mu-plugins/myprefix-lmp.php
add_action( 'link_manager_pro_booted', 'myprefix_after_boot' );
function myprefix_after_boot( $app ): void {
	define( 'MYPREFIX_LMP_READY', true );
}
```

## Settings

### `link_manager_pro_settings_updated`

Runs your code after a settings group is saved.

**When it runs:** after the new values are stored, for every save of a group or of a single setting, including a settings restore from a backup file. It runs even when no value changed.

**Parameters:**
- `$group` (string): the settings group, such as `tracking` or `advanced`.
- `$new` (array): all values of that group after the save, keyed by setting name. Encrypted keys such as the AI API key are masked: `lmpro_masked:` plus the last 4 characters, and an empty key stays empty. A listener that needs the key reads it with `Settings::get( 'ai.api_key' )`. Two keys with the same last 4 characters look unchanged in `$new` and `$old`.
- `$old` (array): all values of that group before the save.

**Source:** `app/Services/Settings/SettingsManager.php`

**Usage:**

```php
add_action( 'link_manager_pro_settings_updated', 'myprefix_settings_saved', 10, 3 );
function myprefix_settings_saved( string $group, array $new, array $old ): void {
	if ( 'tracking' === $group && ( $new['track_clicks'] ?? '' ) !== ( $old['track_clicks'] ?? '' ) ) {
		error_log( 'Click tracking is now ' . $new['track_clicks'] );
	}
}
```

## Caching

### `link_manager_pro_cache_should_purge`

Asks page caches to clear because link content changed.

**When it runs:** after redirects are added or changed, after auto-link rules, affiliates or link prefixes change, after a short link address changes and after a bulk import. The plugin's own listener purges the supported cache plugins, unless **Clear page cache when links change** is off. It purges at most once every 30 seconds, and a trigger inside that window schedules one trailing purge.

**Parameters:** none.

**Source:** `app/Hooks/Handlers/CachingIntegrationHandler.php`, `app/Support/LinkAddress.php`, `app/Services/Redirects/SimpleRedirectService.php`, `app/Services/Content/AutoLinkService.php`

**Usage:**

```php
add_action( 'link_manager_pro_cache_should_purge', 'myprefix_purge_cdn' );
function myprefix_purge_cdn(): void {
	wp_remote_post( 'https://example.com/purge-my-cdn', array( 'blocking' => false ) );
}
```

## Related

- [Hooks overview](https://docs.linkmanagerpro.com/developers/hooks/)
- [Filter hooks](https://docs.linkmanagerpro.com/developers/hooks/filters)
- [Events, webhooks and AutomatorWP](https://docs.linkmanagerpro.com/developers/hooks/events)
- [Webhooks and AutomatorWP](https://docs.linkmanagerpro.com/guides/webhooks-automation)
