Customizing Share a Draft with code, WP-CLI, and Abilities
Share a Draft works without configuration. When a site needs different behavior, filters and actions change how links behave, from the lifetimes offered to what reviewers are told. The same operations available in the block editor are also available from WP-CLI and from the WordPress Abilities API, so scripts and AI assistants can create and manage links under the same rules.
Add the code on this page to a plugin in the application’s repository. On the WordPress VIP Platform, code reaches an environment through deployment, not through the WordPress Admin dashboard.
Prerequisites
To customize Share a Draft, the following must be in place:
- Share a Draft must be enabled for the application.
- The site must run WordPress 6.9 or later, which includes the Abilities API.
- For WP-CLI, VIP-CLI must be installed and authenticated for the application.
Limitations
- Lifetimes, limits, and restrictions are fixed when a link is created, and there is no command or ability to edit a link. To change a link, revoke it and create a new one.
- Turning off a restriction, such as named reviewers, stops new links from using it. Links that already use the restriction keep enforcing it.
- Code-based settings take precedence over settings in the VIP Dashboard.
Changing link lifetimes
The editor offers links that last 1 hour, 8 hours, 24 hours, or 7 days. To offer other lifetimes, return a list from the shareadraft_expiration_options filter. Each option is a number of seconds and a label, in the order they should appear:
add_filter(
'shareadraft_expiration_options',
function ( array $options ): array {
$options[] = array(
'seconds' => 30 * DAY_IN_SECONDS,
'label' => __( '30 days', 'my-plugin' ),
);
return $options;
}
);
Only lifetimes in this list are accepted, whether a link is created in the editor, with WP-CLI, or through the Abilities API.
To change which lifetime is selected when the editor opens, return a number of seconds from shareadraft_default_expiration:
add_filter( 'shareadraft_default_expiration', fn () => DAY_IN_SECONDS );
Turning off reviewer or IP restrictions
Named reviewers and IP restrictions are optional. To switch either off for the site, return false from the matching filter:
add_filter( 'shareadraft_recipients_enabled', '__return_false' );
add_filter( 'shareadraft_ip_allowlist_enabled', '__return_false' );
The fields disappear from the editor, the Preview Links screen, and the Abilities API, and WP-CLI refuses to use them.
Changing the verification email
When a link is bound to named reviewers, each reviewer is emailed a 6-digit code. To change the subject or wording, use the shareadraft_verification_email filter. It receives the subject and plain-text message, the recipient’s address, and the code. If the message is replaced entirely, it must still include the code:
add_filter(
'shareadraft_verification_email',
function ( array $mail, string $email, string $code ): array {
/* translators: %s: Site title. */
$mail['subject'] = sprintf( __( 'Your review code for %s', 'my-plugin' ), get_bloginfo( 'name' ) );
/* translators: %s: Six-digit verification code. */
$mail['message'] = sprintf( __( 'Your code is %s. It expires in a few minutes; please do not share it.', 'my-plugin' ), $code );
return $mail;
},
10,
3
);
Changing what reviewers are told
When a link has expired, been revoked, or been used up, the reviewer sees a short notice on a simple page that carries the site’s name and icon. The email verification steps use the same page.
To change the text inside the page, use shareadraft_notice_content. It receives the page’s HTML content, its title, and its HTTP status. Whatever the filter returns is output as is, so escape anything added. To replace the whole page, hook the wp_die_handler filter, because every notice is shown through wp_die().
To say less about why a link stopped working, return false from shareadraft_disclose_denial_reason. Every reason then collapses into one general message. The filter also receives the reason (expired, revoked, exhausted, or links_disabled), so individual reasons can be hidden:
// Say when a link has expired, but not when it has been revoked.
add_filter(
'shareadraft_disclose_denial_reason',
fn ( bool $disclose, string $reason ): bool => 'revoked' !== $reason,
10,
2
);
Only someone holding a genuine link for that draft reaches these notices, so this is a matter of tone, not security.
Changing how long expired links are kept
Expired and revoked links are kept for 21 days, so a returning reviewer is told why the link stopped working. A daily cleanup then deletes them. To change the period, return a number of seconds from shareadraft_dead_link_grace_period:
add_filter( 'shareadraft_dead_link_grace_period', fn () => 7 * DAY_IN_SECONDS );
Returning 0 deletes expired and revoked links at the next cleanup. The retention period can also be set with the Expired link retention setting in the VIP Dashboard. The code filter takes precedence.
Recognizing more link previewers
Chat apps and social networks fetch a link as soon as it is pasted. Share a Draft recognizes these services and crawlers by user agent and shows them an empty placeholder, so they neither see the draft nor use one of the link’s views. To add another service, extend the regular expression in shareadraft_bot_user_agent_pattern:
add_filter(
'shareadraft_bot_user_agent_pattern',
fn ( string $pattern ): string => str_replace( '/bot|', '/bot|mattermost|', $pattern )
);
Revoking a person’s links on other events
A person’s links are revoked automatically when their user account is deleted, but not when their role changes. To revoke them on other events, run the shareadraft_revoke_user_links action with the user’s ID:
// Revoke a user's preview links when they lose edit access.
add_action(
'set_user_role',
function ( int $user_id, string $role ): void {
if ( ! in_array( $role, array( 'administrator', 'editor', 'author' ), true ) ) {
do_action( 'shareadraft_revoke_user_links', $user_id );
}
},
10,
2
);
// Multisite: revoke when a user is removed from this site.
add_action(
'remove_user_from_blog',
function ( int $user_id ): void {
do_action( 'shareadraft_revoke_user_links', $user_id );
}
);
After all of a user’s links have been revoked, however the revocation was triggered, the shareadraft_revoked_user_links action runs with the user’s ID, the number of links revoked, and the ID of the user who started it. The third value is 0 when the revocation happened automatically. Use the action to record offboarding in an audit log:
add_action(
'shareadraft_revoked_user_links',
function ( int $user_id, int $count, int $actor ): void {
// For example, send to an audit log.
},
10,
3
);
Managing links with WP-CLI
Share a Draft adds a wp shareadraft command. A link created or revoked with WP-CLI follows the same rules as one created in the editor.
| Command | Description |
|---|---|
wp shareadraft create <post-id> | Creates a link and prints its URL. This is the only moment the URL exists in plain text. |
wp shareadraft list [<post-id>] | Lists a post’s live links, or every live link on the site when no post is given. |
wp shareadraft revoke [<post-id>] [<link>] | Revokes one link, all of a post’s links, or every link a user created. |
wp shareadraft disable | Pauses every preview link on the site. |
wp shareadraft enable | Switches paused links back on. |
wp shareadraft prune | Deletes expired and revoked links past their retention period immediately. |
Options for create:
| Option | Description |
|---|---|
--expiration=<seconds> | How long the link lasts. Must be one of the lifetimes on offer. |
--max-uses=<count> | Maximum number of viewers. |
--allowed-ips=<ranges> | Addresses or ranges allowed to open the link. |
--recipients=<emails> | Reviewers who must confirm their email address. |
--porcelain | Prints only the URL, for use in scripts. |
Run wp help shareadraft <command> for every option.
WP-CLI runs without a logged-in user, so a link created from the command line is attributed to no one unless the global --user= flag is passed. Links attributed to no one do not appear under a person’s name on the Preview Links screen and are not revoked when a person leaves.
Using Abilities
Share a Draft registers its operations with the Abilities API, so software acting for a user can create and manage links. This includes AI assistants connected through the MCP Adapter, the WordPress AI Client, and scripts that call the abilities REST endpoints.
An ability runs as the WordPress user who authorized the client and applies the same permission checks as the block editor and WP-CLI. An assistant can never do more with a link than the person it acts for.
All 6 abilities are in the shareadraft category:
| Ability | Description | Who can use it |
|---|---|---|
shareadraft/create-preview-link | Creates a link for a draft with the same options as the editor and returns its URL. | Anyone who can edit the post |
shareadraft/list-preview-links | Lists a post’s working links, or every working link on the site. Entries show usage, expiry, and the last four characters of the token, never the URL. | Anyone who can edit the post; editors and administrators for the whole site |
shareadraft/revoke-preview-link | Revokes one link, all of a post’s links, everything one person created, or every link on the site. | Anyone who can edit the post; editors for one person’s links; administrators for the whole site |
shareadraft/prune-preview-links | Deletes expired and revoked links past their retention period. | Administrators |
shareadraft/set-preview-links-enabled | Pauses every link on the site, or lets links work again. | Administrators |
shareadraft/get-preview-links-status | Reports whether links work and, if paused, who paused them and when. | Anyone who can edit posts |
Because the URL of a link is shown only when it is created, an assistant that needs to share a link must capture the URL returned by shareadraft/create-preview-link. Listing links later cannot retrieve it.
Last updated: October 20, 2026