Title: rest-api – Make WordPress Core

---

#  Tag Archives: rest-api

 [  ](https://profiles.wordpress.org/milana_cap/) [Milana Cap](https://profiles.wordpress.org/milana_cap/)
11:55 am _on_ August 5, 2026     
Tags: [#abilities-api ( 6 )](https://make.wordpress.org/core/tag/abilities-api/),
[7.1 ( 68 )](https://make.wordpress.org/core/tag/7-1/), [dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/),
[dev-notes-7-1 ( 28 )](https://make.wordpress.org/core/tag/dev-notes-7-1/), rest-
api   

# 󠀁[Filtering registered abilities with wp_get_abilities() in WordPress 7.1](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/)󠁿

WordPress 7.1 extends `[wp_get_abilities()](https://developer.wordpress.org/reference/functions/wp_get_abilities/)`
with a standard way to filterFilter Filters are one of the two types of Hooks [https://codex.wordpress.org/Plugin_API/Hooks](https://codex.wordpress.org/Plugin_API/Hooks).
They provide a way for functions to modify data of other functions. They are the
counterpart to Actions. Unlike Actions, filters are meant to work in an isolated
manner, and should never have side effects such as affecting global variables and
output. registered abilities.

The function now accepts an optional `$args` array that can filter abilities by `
category`, `namespace`, or `metadata`. It also supports callbacks for custom per-
item filtering and final result processing.

Two new WordPress filters allow plugins to influence ability retrieval across the
site:

 * `wp_get_abilities_item_include`
 * `wp_get_abilities_result`

The REST APIREST API The REST API is an acronym for the RESTful Application Program
Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is
how the front end of an application (think “phone app” or “website”) can communicate
with the data store (think “database” or “file system”) [https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/)’
s abilities list controller now uses `wp_get_abilities()` instead of implementing
categoryCategory The 'category' taxonomy lets you group posts / content together
that share a common bond. Categories are pre-defined and broad ranging. filtering
separately. It also supports filtering abilities by namespace.

 1.  [Why was this change needed?](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#why-was-this-change-needed)
 2.  [Filtering by category](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#filtering-by-category)
 3.  [Filtering by namespace](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#filtering-by-namespace)
 4.  [Filtering by metadata](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#filtering-by-metadata)
 5.  [Combining declarative filters](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#combining-declarative-filters)
 6.  [Custom per-item filtering](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#custom-per-item-filtering)
 7.  [Processing the complete result](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#processing-the-complete-result)
 8.  [New global filters](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#new-global-filters)
 9.   a. [wp_get_abilities_item_include](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#wp-get-abilities-item-include)
      b. [wp_get_abilities_result](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#wp-get-abilities-result)
 10. [Filtering order](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#filtering-order)
 11. [Discovery over REST](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#discovery-over-rest)
 12.  a. [Built-in annotation types](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#built-in-annotation-types)
 13. [Backward compatibility](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#backward-compatibility)
 14. [Retrieving the raw registry](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#retrieving-the-raw-registry)
 15. [Filtering does not replace authorisation](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#filtering-does-not-replace-authorisation)
 16. [When to use each option](https://make.wordpress.org/core/2026/08/05/filtering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1/#when-to-use-each-option)

## Why was this change needed?

Before WordPress 7.1, there were two ways to retrieve abilities:

    ```notranslate
    // Retrieve every registered ability.
    $abilities = wp_get_abilities();

    // Retrieve one named ability.
    $ability = wp_get_ability( 'my-plugin/export-users' );
    ```

`wp_get_abilities()` always returned the complete registry. A caller needing a subset
had to retrieve every ability and filter the result manually:

    ```notranslate
    $abilities = array_filter(
    	wp_get_abilities(),
    	function ( WP_Ability $ability ): bool {
    		return 'data-export' === $ability->get_category();
    	}
    );
    ```

Several consumers developed their own versions of this pattern for category, namespace,
and metadata checks. The REST abilities controller also performed its own category
filtering after retrieving the complete registry.

This led to:

 * Duplicated filtering code.
 * Inconsistent filtering semantics between consumers.
 * Different behaviour between the PHPPHP The web scripting language in which WordPress
   is primarily architected. WordPress requires PHP 7.4 or higher and REST APIs.
 * No standard extension points for ability selection.
 * Additional filtering passes over the registry.

WordPress 7.1 moves this work into `wp_get_abilities()`, providing one shared filtering
pipeline for CoreCore Core is the set of software required to run WordPress. The
Core Development Team builds WordPress. and plugins.

## Filtering by category

Pass a category slug using the `category` argument:

    ```notranslate
    $abilities = wp_get_abilities(
    	array(
    		'category' => 'data-export',
    	)
    );
    ```

The comparison is exact. Only abilities whose category exactly matches the supplied
string are returned.

The category must be passed as a single string. Arrays of category slugs are not
supported.

## Filtering by namespace

Use `namespace` to retrieve abilities registered under a particular namespace:

    ```notranslate
    $abilities = wp_get_abilities(
    	array(
    		'namespace' => 'my-plugin',
    	)
    );
    ```

The namespace is passed without the trailing slash. Both of the following values
are normalised to the same namespace:

    ```notranslate
    'namespace' => 'my-plugin',
    'namespace' => 'my-plugin/',
    ```

An ability such as `my-plugin/export-users` matches, while `another-plugin/export-
users` does not.

Namespace matching includes the namespace delimiter. Passing `my-plugin` does not
accidentally match an ability registered under a similarly named `my-plugin-extra`
namespace.

## Filtering by metadata

The `meta` argument selects abilities whose metadata contains the specified key-
value pairs:

    ```notranslate
    $abilities = wp_get_abilities(
    	array(
    		'meta' => array(
    			'public' => true,
    		),
    	)
    );
    ```

All supplied metadata conditions must match:

    ```notranslate
    $abilities = wp_get_abilities(
    	array(
    		'meta' => array(
    			'public'       => true,
    			'show_in_rest' => true,
    		),
    	)
    );
    ```

Nested metadata is supported:

    ```notranslate
    $abilities = wp_get_abilities(
    	array(
    		'meta' => array(
    			'my_client' => array(
    				'public' => true,
    			),
    		),
    	)
    );
    ```

Metadata comparisons are strict. The value `true` does not match `1`, and `false`
does not match `0`.

The metadata filter checks that every requested condition exists and matches. An
ability may contain additional metadata that was not included in the query.

## Combining declarative filters

The `category`, `namespace`, and `meta` arguments can be combined:

    ```notranslate
    $abilities = wp_get_abilities(
    	array(
    		'category'  => 'data-export',
    		'namespace' => 'my-plugin',
    		'meta'      => array(
    			'public' => true,
    		),
    	)
    );
    ```

Conditions are combined using `AND` logic. An ability must satisfy every supplied
argument to be included.

In this example, the result contains only abilities that:

 * Belong to the `data-export` category.
 * Use the `my-plugin` namespace.
 * Have resolved `public` metadata set to `true`.

## Custom per-item filtering

Conditions that cannot be expressed using the declarative arguments can be handled
with `item_include_callback`:

    ```notranslate
    $abilities = wp_get_abilities(
    	array(
    		'namespace'             => 'my-plugin',
    		'item_include_callback' => function (
    			WP_Ability $ability
    		): bool {
    			return my_plugin_should_include_ability( $ability );
    		},
    	)
    );
    ```

The callback runs once for every ability that passed the declarative filters. It
receives the `WP_Ability` instance and must return a boolean:

 * Return `true` to include the ability.
 * Return `false` to exclude it.

This callback is scoped to the current `wp_get_abilities()` call. It does not affect
ability retrieval elsewhere.

Use it for conditions such as:

 * Custom metadata relationships.
 * Context-dependent visibility.
 * Integration-specific rules.
 * Conditions involving more than one ability property.

## Processing the complete result

Use `result_callback` when an operation requires the complete matched array:

    ```notranslate
    $abilities = wp_get_abilities(
    	array(
    		'namespace'       => 'my-plugin',
    		'result_callback' => function ( array $abilities ): array {
    			uasort(
    				$abilities,
    				function (
    					WP_Ability $first,
    					WP_Ability $second
    				): int {
    					return strcasecmp(
    						$first->get_label(),
    						$second->get_label()
    					);
    				}
    			);

    			return array_slice(
    				$abilities,
    				0,
    				10,
    				true
    			);
    		},
    	)
    );
    ```

The result callback runs after all per-item matching has completed. It is suitable
for:

 * Sorting.
 * Slicing or pagination.
 * Reordering.
 * Other final result transformations.

Like `item_include_callback`, `result_callback` applies only to the current function
call.

Registered abilities are normally returned in an associative array keyed by ability
name. When sorting or slicing the result, preserve those keys when downstream code
depends on them.

## New global filters

WordPress 7.1 also introduces two filters for plugins that need to affect ability
retrieval beyond a single call site.

### `wp_get_abilities_item_include`

The `wp_get_abilities_item_include` filter runs for every ability that passed the
declarative conditions and the caller’s `item_include_callback`:

    ```notranslate
    add_filter(
    	'wp_get_abilities_item_include',
    	function (
    		bool $include,
    		WP_Ability $ability,
    		array $args
    	): bool {
    		if ( 'my-plugin/private-operation' === $ability->get_name() ) {
    			return false;
    		}

    		return $include;
    	},
    	10,
    	3
    );
    ```

The filter receives:

 * `$include`: Whether the ability should currently be included.
 * `$ability`: The ability being evaluated.
 * `$args`: The complete arguments passed to `wp_get_abilities()`.

Because declarative mismatches are removed before this filter runs, the filter cannot
add an ability that failed `category`, `namespace`, or `meta` matching. It can influence
the inclusion of abilities that have reached this stage, as well as their global
exclusion.

### `wp_get_abilities_result`

The `wp_get_abilities_result` filter receives the complete result after the caller’s`
result_callback`:

    ```notranslate
    add_filter(
    	'wp_get_abilities_result',
    	function ( array $abilities, array $args ): array {
    		// Apply site-wide result processing when appropriate.
    		return $abilities;
    	},
    	10,
    	2
    );
    ```

The filter receives:

 * `$abilities`: The final matched array.
 * `$args`: The complete arguments passed to `wp_get_abilities()`.

It can be used for site-wide sorting, reordering, or other final processing.

These are global filters. Plugins should use them only when the behaviour is intended
to affect every relevant caller. For logic that belongs to one operation, prefer`
item_include_callback` or `result_callback`.

## Filtering order

The complete pipeline runs in the following order:

 1. Match the `category` argument.
 2. Match the `namespace` argument.
 3. Match the `meta` argument.
 4. Run `item_include_callback`.
 5. Apply `wp_get_abilities_item_include`.
 6. Add included abilities to the matched result.
 7. Run `result_callback` on the complete result.
 8. Apply `wp_get_abilities_result`.

The declarative checks, item callback, and item filter run within a single pass 
over the registry.

This avoids the separate `array_filter()` passes that consumers previously had to
implement.

## Discovery over REST

The REST collection endpoint delegates to `wp_get_abilities()` and exposes the declarative
filters as query parameters:

    ```notranslate
    GET /wp-json/wp-abilities/v1/abilities?namespace=my-plugin
    GET /wp-json/wp-abilities/v1/abilities?category=my-plugin-content
    GET /wp-json/wp-abilities/v1/abilities?meta[annotations][readonly]=true
    ```

Parameters can be combined and use the same `AND` logic.

    ```notranslate
    ?category=data-export&namespace=my-plugin
    ```

Every collection request also forces `meta.show_in_rest = true` internally. Supplying
another metadata query can’t reveal an ability that is hidden from REST. The endpoint
still requires an authenticated WordPress user, and executing a listed ability still
requires its permission callback to pass.

Custom metadata needs a REST parameter schema if its query-string values should 
be coerced before strict comparison. Without one, `'true'` will never match boolean`
true`. The `rest_abilities_collection_params` filter extends the collection argument
schema:

    ```notranslate
    add_filter(
    	'rest_abilities_collection_params',
    	static function ( array $params ): array {
    		$params['meta']['properties']['my_plugin'] = array(
    			'type'       => 'object',
    			'properties' => array(
    				'enabled' => array(
    					'type' => 'boolean',
    				),
    			),
    		);
    		return $params;
    	}
    );
    ```

After that declaration, the REST API casts `"true"` to a boolean `true` before the
value reaches the metadata-matching logic.

    ```notranslate
    GET /wp-json/wp-abilities/v1/abilities?meta[my_plugin][enabled]=true
    ```

### Built-in annotation types

The known `readonly`, `destructive`, and `idempotent` annotation values are coerced
from query strings to boolean values before strict matching. Core declares the schema
for these standard ability annotations.

Each accepts a boolean or `null`, so REST can correctly cast their query values 
without a pluginPlugin A plugin is a piece of software containing a group of functions
that can be added to a WordPress website. They can extend functionality or add new
features to your WordPress websites. WordPress plugins are written in the PHP programming
language and integrate seamlessly with WordPress. These can be free in the WordPress.
org Plugin Directory [https://wordpress.org/plugins/](https://wordpress.org/plugins/)
or can be cost-based plugin from a third-party. extending the schema:

    ```notranslate
    ?meta[annotations][readonly]=true
    ```

Use `rest_abilities_collection_params` filter when making additional metadata fields
queryable, especially boolean, integer, number, array, or object values that cannot
be matched correctly as untyped query strings.

## Backward compatibility

The `$args` parameter is optional:

    ```notranslate
    $abilities = wp_get_abilities();
    ```

Existing calls remain valid, and the function still returns an array of `WP_Ability`
instances keyed by ability name.

Code that manually filters the result can continue to work:

    ```notranslate
    $abilities = array_filter(
    	wp_get_abilities(),
    	'my_plugin_filter_abilities'
    );
    ```

However, plugins should migrate common category, namespace, and metadata checks 
to the new arguments. Doing so reduces duplicated code and allows Core and other
integrations to use consistent matching behaviour.

One behavioural detail deserves particular attention: the two new global filters
run even when `wp_get_abilities()` is called without arguments.

As a result, the following call now means “retrieve abilities through the standard
filtering pipeline”:

    ```notranslate
    $abilities = wp_get_abilities();
    ```

It does not necessarily mean “retrieve raw registry contents,” because another plugin
can alter the result through `wp_get_abilities_item_include` and `wp_get_abilities_result`.

## Retrieving the raw registry

Code that specifically needs the complete, unfiltered registry can use `WP_Abilities_Registry::
get_all_registered()`:

    ```notranslate
    $registry  = WP_Abilities_Registry::get_instance();
    $abilities = $registry->get_all_registered();
    ```

This bypasses:

 * Declarative filtering.
 * Caller callbacks.
 * `wp_get_abilities_item_include`
 * `wp_get_abilities_result`

Most application and integration code should continue using `wp_get_abilities()`.
Direct registry access is appropriate only when raw registered state is explicitly
required, such as low-level debugging or registry inspection.

## Filtering does not replace authorisation

Filtering controls which abilities are returned during discovery. It does not determine
whether the current user may execute an ability.

An ability’s `permission_callback` remains responsible for authorisation:

    ```notranslate
    'permission_callback' => function (): bool {
    	return current_user_can( 'manage_options' );
    },
    ```

Developers should not assume that an ability returned by `wp_get_abilities()` is
executable by the current user.

Similarly, excluding an ability from a filtered result is not a security boundary.
Any sensitive operation must enforce its permissions when the ability is executed.

## When to use each option

Use declarative arguments for standard selection:

    ```notranslate
    wp_get_abilities(
    	array(
    		'category'  => 'data-export',
    		'namespace' => 'my-plugin',
    		'meta'      => array(
    			'public' => true,
    		),
    	)
    );
    ```

Use `item_include_callback` for custom conditions that apply to one call:

    ```notranslate
    wp_get_abilities(
    	array(
    		'item_include_callback' => 'my_plugin_should_include_ability',
    	)
    );
    ```

Use `result_callback` for call-specific sorting or slicing:

    ```notranslate
    wp_get_abilities(
    	array(
    		'result_callback' => 'my_plugin_prepare_ability_results',
    	)
    );
    ```

Use `wp_get_abilities_item_include` or `wp_get_abilities_result` only for behaviour
intended to affect ability retrieval across callers.

Use `WP_Abilities_Registry::get_all_registered()` only when code explicitly requires
raw, unfiltered registry data.

Together, these changes make `wp_get_abilities()` the shared discovery and filtering
primitive for the Abilities APIAPI An API or Application Programming Interface is
a software intermediary that allows programs to interact with each other and share
data in limited, clearly defined ways., replacing duplicated filtering implementations
with a consistent, extensibleExtensible This is the ability to add additional functionality
to the code. Plugins extend the WordPress core software. pipeline.

These changes were introduced in changeset [[62420]](https://core.trac.wordpress.org/changeset/62420)
for TracTrac An open source project by Edgewall Software that serves as a bug tracker
and project management tool for WordPress. ticketticket Created for both bug reports
and feature development on the bug tracker. [#64990](https://core.trac.wordpress.org/ticket/64990).

_Props to [@benjamin\_zekavica](https://profiles.wordpress.org/benjamin_zekavica/)
for peer review, and [@gziolo](https://profiles.wordpress.org/gziolo/) for review,
technical guidance, and suggested improvements._

[#abilities-api](https://make.wordpress.org/core/tag/abilities-api/), [#7-1](https://make.wordpress.org/core/tag/7-1/),
[#dev-notes](https://make.wordpress.org/core/tag/dev-notes/), [#dev-notes-7-1](https://make.wordpress.org/core/tag/dev-notes-7-1/),
[#rest-api](https://make.wordpress.org/core/tag/rest-api/)

 * [Login to Reply](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fmake.wordpress.org%2Fcore%2F2026%2F08%2F05%2Ffiltering-registered-abilities-with-wp_get_abilities-in-wordpress-7-1%2F%23respond&locale=en_US)

 [  ](https://profiles.wordpress.org/milana_cap/) [Milana Cap](https://profiles.wordpress.org/milana_cap/)
12:49 pm _on_ August 4, 2026     
Tags: [#abilities-api ( 6 )](https://make.wordpress.org/core/tag/abilities-api/),
[7.1 ( 68 )](https://make.wordpress.org/core/tag/7-1/), [dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/),
[dev-notes-7-1 ( 28 )](https://make.wordpress.org/core/tag/dev-notes-7-1/), rest-
api   

# 󠀁[A unified public exposure flag for Abilities in WordPress 7.1](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/)󠁿

WordPress 7.1 introduces a new `public` metadata flag for abilities. The flag provides
a single, high-level way to indicate that an ability is intended to be available
to external clients such as the REST APIREST API The REST API is an acronym for 
the RESTful Application Program Interface (API) that uses HTTP requests to GET, 
PUT, POST and DELETE data. It is how the front end of an application (think “phone
app” or “website”) can communicate with the data store (think “database” or “file
system”) [https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/),
MCP adapters, and AI agents.

Table of contents:

 1.  [Registering a public ability](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/#registering-a-public-ability)
 2.  [How exposure defaults are resolved](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/#how-exposure-defaults-are-resolved)
 3.  [What problem does this change fix?](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/#what-problem-does-this-change-fix)
 4.  [Using public in other integrations](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/#using-public-in-other-integrations)
 5.  [Exposure is not authorisation](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/#exposure-is-not-authorisation)
 6.  [Changes to resolved metadata](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/#changes-to-resolved-metadata)
 7.  [Backward compatibility](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/#backward-compatibility)
 8.   a. [Existing Core abilities](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/#existing-core-abilities)
 9.  [When to use each flag](https://make.wordpress.org/core/2026/08/04/a-unified-public-exposure-flag-for-abilities-in-wordpress-7-1/#when-to-use-each-flag)

Previously, ability authors had to express that intent separately for every exposure
channel. For example, an ability exposed through the REST API needed to set `show_in_rest`
directly:

    ```notranslate
    'meta' => array(
    	'show_in_rest' => true,
    ),
    ```

As the Abilities APIAPI An API or Application Programming Interface is a software
intermediary that allows programs to interact with each other and share data in 
limited, clearly defined ways. gains more client integrations, repeating the same
intent through several channel-specific flags becomes difficult to maintain. The
new `public` flag establishes a common default while preserving granular control
for each channel.

## Registering a public ability

An ability intended for client exposure can now set `meta.public` when it is registered:

    ```notranslate
    function my_plugin_register_abilities(): void {
    	wp_register_ability(
    		'my-plugin/export-users',
    		array(
    			'label'               => __( 'Export users', 'my-plugin' ),
    			'description'         => __( 'Exports user data as CSV.', 'my-plugin' ),
    			'category'            => 'data-export',
    			'execute_callback'    => 'my_plugin_export_users',
    			'permission_callback' => function (): bool {
    				return current_user_can( 'export' );
    			},
    			'meta'                => array(
    				'public' => true,
    			),
    		)
    	);
    }

    add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' );
    ```

For the REST API, setting `public` to `true` makes `show_in_rest` default to `true`.
The ability can therefore be discovered and invoked through the REST abilities endpoints,
subject to its permission callback.

## How exposure defaults are resolved

Channel-specific settings take precedence over the general `public` setting. The
effective REST exposure value is resolved as follows:

    ```notranslate
    $show_in_rest = $meta['show_in_rest'] ?? $meta['public'] ?? false;
    ```

For example, an ability that is generally public but must not be exposed through
REST can use:

    ```notranslate
    'meta' => array(
    	'public'       => true,
    	'show_in_rest' => false,
    ),
    ```

The explicit `show_in_rest` value wins over `public`.

Conversely, an ability that is not generally public can still opt into REST specifically:

    ```notranslate
    'meta' => array(
    	'public'       => false,
    	'show_in_rest' => true,
    ),
    ```

The resolution uses null-coalescing semantics so an explicit `false` is preserved.
It is not treated as a missing value.

A `null` value is treated as unset and falls back to the next value in the chain.

In practical terms:

| Registration metadata | Effective `public` | Effective `show_in_rest` | 
| No exposure metadata | `false` | `false` | 
| `public => true` | `true` | `true` | 
| `public => false` | `false` | `false` | 
| `show_in_rest => true` | `false` | `true` | 
| `public => true`, `show_in_rest => false` | `true` | `false` | 
| `public => false`, `show_in_rest => true` | `false` | `true` |

This precedence allows for a broad exposure default while opting in or out of individual
channels.

## What problem does this change fix?

Ability metadata already contained channel-specific exposure settings such as `show_in_rest`.
As support for MCP, AI agents, and other clients develops, requiring ability authors
to configure each channel independently would duplicate the same policy across multiple
properties:

    ```notranslate
    'meta' => array(
    	'show_in_rest' => true,
    	'mcp'          => array(
    		'public' => true,
    	),
    	// Additional flags for future clients.
    ),
    ```

This also makes it difficult for a newly introduced channel to determine whether
an existing ability was intended for external use.

The `public` flag fixes this by recording the ability author’s general exposure 
intent in one stable location:

    ```notranslate
    'meta' => array(
    	'public' => true,
    ),
    ```

Individual integrations can use that value as their default while retaining a more
specific channel-level override.

REST is the first built-in consumer of this behaviour. Other integrations can adopt
the same default without adding channel-specific logic to WordPress CoreCore Core
is the set of software required to run WordPress. The Core Development Team builds
WordPress..

## Using `public` in other integrations

The resolved `public` value remains available in the ability’s metadata. Client 
integrations can inspect this value when determining whether an ability should be
exposed.

The WordPress MCP Adapter [will respect the unified `public` flag](https://github.com/WordPress/mcp-adapter/pull/254)
starting with its next release. WP-CLIWP-CLI WP-CLI is the Command Line Interface
for WordPress, used to do administrative and development tasks in a programmatic
way. The project page is [http://wp-cli.org/](http://wp-cli.org/) [https://make.wordpress.org/cli/](https://make.wordpress.org/cli/)
does not apply this exposure check because its ability-listing functionality returns
all registered abilities.

Other integrations should generally resolve exposure when abilities are selected
for that integration:

    ```notranslate
    function my_plugin_is_ability_exposed(
    	WP_Ability $ability,
    	string $channel
    ): bool {
    	$meta = $ability->get_meta();

    	return $meta[ $channel ]['public'] ?? $meta['public'] ?? false;
    }
    ```

Integrations that need to derive their own channel-specific metadata during registration
can use the existing `[wp_register_ability_args](https://developer.wordpress.org/reference/hooks/wp_register_ability_args/)`
filterFilter Filters are one of the two types of Hooks [https://codex.wordpress.org/Plugin_API/Hooks](https://codex.wordpress.org/Plugin_API/Hooks).
They provide a way for functions to modify data of other functions. They are the
counterpart to Actions. Unlike Actions, filters are meant to work in an isolated
manner, and should never have side effects such as affecting global variables and
output.:

    ```notranslate
    add_filter(
    	'wp_register_ability_args',
    	function ( array $args, string $name ): array {
    		if (
    			! isset( $args['meta']['my_client']['public'] )
    			&& isset( $args['meta']['public'] )
    		) {
    			$args['meta']['my_client']['public'] =
    				(bool) $args['meta']['public'];
    		}

    		return $args;
    	},
    	10,
    	2
    );
    ```

An integration should follow the same precedence rule as REST:

 1. Use an explicit channel-specific value when present.
 2. Otherwise, inherit `public`.
 3. Otherwise, use the channel’s built-in default.

Integrations should not overwrite an explicit channel opt-out merely because `public`
is true.

## Exposure is not authorisation

The `public` flag controls discoverability and client exposure. It does not make
an ability executable without authorisation, and it does not replace the ability’s`
permission_callback`.

Every ability must continue to implement an appropriate permission check:

    ```notranslate
    'permission_callback' => function (): bool {
    	return current_user_can( 'manage_options' );
    },
    ```

An ability with `public => true` may be visible through a client while still requiring
authentication and specific WordPress capabilitiescapability A **capability** is
permission to perform one or more types of task. Checking if a user has a capability
is performed by the `current_user_can` function. Each user of a WordPress site might
have some permissions but not others, depending on their role. For example, users
who have the Author role usually have permission to edit their own posts (the “edit_posts”
capability), but not permission to edit other users’ posts (the “edit_others_posts”
capability). to execute.

Developers should not treat `public`, `show_in_rest`, or any other exposure flag
as a security boundary. Authorisation must be enforced by the ability itself.

## Changes to resolved metadata

In WordPress 7.1, the resolved metadata for every ability includes a boolean `public`
property. It defaults to `false` when it is not supplied during registration.

For example:

    ```notranslate
    $ability = wp_get_ability( 'my-plugin/export-users' );
    $meta    = $ability->get_meta();

    $is_public = $meta['public']; // Always a boolean in WordPress 7.1.
    ```

This gives consumers a consistent value to inspect without having to test whether
the key exists.

The new property is also declared in the REST API’s ability metadata schema, allowing
REST clients to inspect the general exposure intent.

## Backward compatibility

The change does not alter the signature or return value of `[wp_register_ability()](https://developer.wordpress.org/reference/functions/wp_register_ability/)`
or other Abilities API functions.

Existing channel-specific registrations continue to work:

    ```notranslate
    'meta' => array(
    	'show_in_rest' => true,
    ),
    ```

An explicit `show_in_rest` value remains authoritative. Plugins are not required
to replace it with `public`.

Abilities that previously supplied neither `public` nor `show_in_rest` remain unavailable
through REST. Their resolved metadata now contains `public => false`, but their 
exposure behaviour is unchanged.

Developers should consider migrating from `show_in_rest => true` to `public => true`
when an ability is generally intended for use by multiple client types. Continue
using `show_in_rest` directly when exposure is intentionally limited to REST or 
when overriding the general policy.

### Existing Core abilities

The following abilities included with WordPress now use `meta.public` instead of
setting `meta.show_in_rest` directly:

 * `core/get-site-info`
 * `core/get-user-info`
 * `core/get-environment-info`

Their REST availability has not changed. Because `public => true` supplies the default
for `show_in_rest`, these abilities remain exposed through REST as before.

Using the high-level flag also allows other client integrations to recognise that
these Core abilities are intended for external use.

## When to use each flag

Use `public` when the ability is generally intended for consumption by external 
clients.

Use a channel-specific flag when:

 * The ability should be exposed through only that channel.
 * The ability needs to opt out of a channel despite being generally public.
 * A client integration provides behaviour that cannot be represented by the general
   flag.

The change was introduced in changeset [[62729]](https://core.trac.wordpress.org/changeset/62729),
with Core abilities migrated in changeset [[62737]](https://core.trac.wordpress.org/changeset/62737).
See TracTrac An open source project by Edgewall Software that serves as a bug tracker
and project management tool for WordPress. ticketticket Created for both bug reports
and feature development on the bug tracker. [#65568](https://core.trac.wordpress.org/ticket/65568)
for the complete discussion.

_Props to [@gziolo](https://profiles.wordpress.org/gziolo/) and [@benjamin\_zekavica](https://profiles.wordpress.org/benjamin_zekavica/)
for peer review._

[#abilities-api](https://make.wordpress.org/core/tag/abilities-api/), [#7-1](https://make.wordpress.org/core/tag/7-1/),
[#dev-notes](https://make.wordpress.org/core/tag/dev-notes/), [#dev-notes-7-1](https://make.wordpress.org/core/tag/dev-notes-7-1/),
[#rest-api](https://make.wordpress.org/core/tag/rest-api/)

 * [Login to Reply](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fmake.wordpress.org%2Fcore%2F2026%2F08%2F04%2Fa-unified-public-exposure-flag-for-abilities-in-wordpress-7-1%2F%23respond&locale=en_US)

 [  ](https://profiles.wordpress.org/milana_cap/) [Milana Cap](https://profiles.wordpress.org/milana_cap/)
7:15 pm _on_ July 31, 2026     
Tags: [#abilities-api ( 6 )](https://make.wordpress.org/core/tag/abilities-api/),
[7.1 ( 68 )](https://make.wordpress.org/core/tag/7-1/), [dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/),
[dev-notes-7-1 ( 28 )](https://make.wordpress.org/core/tag/dev-notes-7-1/), rest-
api   

# 󠀁[JSON Schema preparation for client compatibility in WordPress 7.1](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/)󠁿

WordPress 7.1 introduces a shared JSONJSON JSON, or JavaScript Object Notation, 
is a minimal, readable format for structuring data. It is used primarily to transmit
data between a server and web application, as an alternative to XML. Schema preparation
layer for schemas exposed to REST clients, frontend applications, and AI tools.

WordPress accepts several internal schema conventions that are useful during server-
side validation but are not portable JSON Schema `draft-04`. Passing these schemas
directly to external validators could cause validation errors or expose PHPPHP The
web scripting language in which WordPress is primarily architected. WordPress requires
PHP 7.4 or higher callbacks and other server-only implementation details.

 1.  [The wp_prepare_json_schema_for_client() function](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#the-wp-prepare-json-schema-for-client-function)
 2.   a. [Compatibility guidance](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#compatibility-guidance)
      b. [Choosing a schema profile](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#choosing-a-schema-profile)
 3.  [Schema transformations](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#schema-transformations)
 4.   a. [Required properties use Draft 4 syntax](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#required-properties-use-draft-4-syntax)
      b. [Server-only keywords are removed](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#server-only-keywords-are-removed)
      c. [Empty object defaults are represented as objects](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#empty-object-defaults-are-represented-as-objects)
 5.  [Allowed keywords filter](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#allowed-keywords-filter)
 6.  [Abilities REST API changes](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#abilities-rest-api-changes)
 7.  [AI Client integration](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#ai-client-integration)
 8.   a. [Follow-up](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#follow-up)
 9.  [Related ticket](https://make.wordpress.org/core/2026/07/31/json-schema-preparation-for-client-compatibility-in-wordpress-7-1/#related-ticket)

## The `wp_prepare_json_schema_for_client()` function

The new `wp_prepare_json_schema_for_client()` function converts a WordPress schema
into a portable, client-facing representation before it is exposed to REST clients,
frontend applications, AI tools, or other external consumers.

Most developers do not need to take action. CoreCore Core is the set of software
required to run WordPress. The Core Development Team builds WordPress. now applies
this preparation automatically to:

 * Abilities APIAPI An API or Application Programming Interface is a software intermediary
   that allows programs to interact with each other and share data in limited, clearly
   defined ways. schemas exposed through REST responses.
 * Ability input schemas converted into AI Client function declarations.

    ```notranslate
    $prepared_schema = wp_prepare_json_schema_for_client( $schema );
    ```

This keeps the schemas used by clients (REST clients, JavaScriptJavaScript JavaScript
or JS is an object-oriented computer programming language commonly used to create
interactive effects within web browsers. WordPress makes extensive use of JS for
a better user experience. While PHP is executed on the server, JS executes within
a user’s browser. [https://www.javascript.com](https://www.javascript.com/) applications,
and AI tools) consistent. See ticketticket Created for both bug reports and feature
development on the bug tracker. [#64955](https://core.trac.wordpress.org/ticket/64955)
and changeset [[62591]](https://core.trac.wordpress.org/changeset/62591).

### Compatibility guidance

This change is primarily automatic. Existing ability registration and execution 
code does not need to call the new function.

Call `wp_prepare_json_schema_for_client()` directly when a pluginPlugin A plugin
is a piece of software containing a group of functions that can be added to a WordPress
website. They can extend functionality or add new features to your WordPress websites.
WordPress plugins are written in the PHP programming language and integrate seamlessly
with WordPress. These can be free in the WordPress.org Plugin Directory [https://wordpress.org/plugins/](https://wordpress.org/plugins/)
or can be cost-based plugin from a third-party. exposes a WordPress-style schema
outside the server-side PHP validation boundary, for example through:

 * a custom REST endpoint;
 * a JavaScript configuration object;
 * an MCP tool declaration;
 * an AI function declaration; or
 * another external schema consumer.

Do not replace the schema stored by an ability with the prepared version. Keep the
canonical WordPress schema for server-side use and prepare a copy only when sending
it to a client.

### Choosing a schema profile

This new function accepts a schema and an optional schema profile:

    ```notranslate
    /** 
     * Prepares a JSON Schema for clients.
     * 
     * @param array<string, mixed> $schema         The schema array.
     * @param string               $schema_profile Optional. Name of the schema
     *                                             profile whose keywords should be
     *                                             preserved. Default 'draft-04'.
     * @return array<string, mixed> The prepared schema.
     */
    wp_prepare_json_schema_for_client(
    	array $schema,
    	string $schema_profile = 'draft-04'
    ): array
    ```

WordPress provides two schema profiles out of the box:

 * `draft-04` **is the default**. Use it when publishing a standalone schema to 
   general-purpose clients, including Ability metadata, frontend validators, MCP
   integrations, and AI tooling. It preserves the broader [JSON Schema Draft 4](https://json-schema.org/draft-04/draft-zyp-json-schema-04)
   vocabulary, including composition and reference keywords such as `$ref`, `definitions`,`
   allOf`, `not`, `dependencies`, and `additionalItems`.
 * `rest-api` uses the narrower keyword set supported by WordPress REST APIREST 
   API The REST API is an acronym for the RESTful Application Program Interface (
   API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the
   front end of an application (think “phone app” or “website”) can communicate 
   with the data store (think “database” or “file system”) [https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/)
   route schemas. Use it when preparing a schema that must follow the same conventions
   as a REST route’s argument or response schema.

    ```notranslate
    // General client-facing or Ability schema.
    $prepared_schema = wp_prepare_json_schema_for_client( $schema );

    // Schema intended to match WordPress REST API conventions.
    $prepared_rest_schema = wp_prepare_json_schema_for_client(
    	$schema,
    	'rest-api'
    );
    ```

Both profiles produce JSON Schema Draft 4 output. The difference is the set of keywords
retained in the prepared schema.

What is JSON Schema Draft 4?

“Draft 4” refers to the fourth published draft of the JSON Schema specification,
which defines a JSON-based contract for describing and validating JSON data. See
the [JSON Schema Draft 4 core specification](https://json-schema.org/draft-04/draft-zyp-json-schema-04)
for its terminology and behaviour.

## Schema transformations

Preparation is recursive and applies to nested object properties, array items, composition
keywords, definitions, dependencies, and other subschemas.

### Required properties use Draft 4 syntax

WordPress schemas may mark individual properties as required:

    ```notranslate
    'properties' => array(
    	'title' => array(
    		'type'     => 'string',
    		'required' => true,
    	),
    )
    ```

The prepared schema moves those property names into the containing object’s Draft
4 `required` array:

    ```notranslate
    'required' => array( 'title' ),
    ```

The property-level boolean is then removed.

If the object already has a valid `required` array, that array takes precedence 
over property-level boolean values. A property-level `required => false` is removed
without creating an empty `required` array.

A boolean `required` value on a scalar schema is also removed because it has no 
Draft 4 equivalent.

This preparation only affects schemas sent to clients. It does not change the server-
side behaviour of `rest_validate_value_from_schema()` or `WP_Ability::validate_input()`.

Example of transforming schema:

    ```notranslate
    $schema = array(
    	'type'       => 'object',
    	'properties' => array(
    		'title'   => array(
    			'type'              => 'string',
    			'required'          => true,
    			'sanitize_callback' => 'sanitize_text_field',
    		),
    		'content' => array(
    			'type'              => 'string',
    			'validate_callback' => 'is_string',
    		),
    	),
    );

    $prepared_schema = wp_prepare_json_schema_for_client( $schema );
    ```

The prepared schema is equivalent to:

    ```notranslate
    array(
    	'type'       => 'object',
    	'required'   => array( 'title' ),
    	'properties' => array(
    		'title'   => array(
    			'type' => 'string',
    		),
    		'content' => array(
    			'type' => 'string',
    		),
    	),
    );
    ```

The original `$schema` is not modified.

### Server-only keywords are removed

PHP callbacks and other WordPress-specific keywords cannot be represented meaningfully
in JSON. The preparation process removes unsupported keywords, including:

 * `sanitize_callback`
 * `validate_callback`
 * `arg_options`

These keywords are removed recursively, including when they appear inside:

 * `properties`
 * `patternProperties`
 * `definitions`
 * `dependencies`
 * `items`
 * `additionalItems`
 * `additionalProperties`
 * `anyOf`
 * `oneOf`
 * `allOf`
 * `not`

The callbacks remain available in the original server-side schema. They are removed
only from its client-facing representation.

Ability authors should also note that `validate_callback` and `sanitize_callback`
are not executed by the Abilities API’s runtime validation. Custom ability validation
should use the `wp_ability_validate_input` and `wp_ability_validate_output` [filters introduced in WordPress 7.1](https://make.wordpress.org/core/2026/07/31/abilities-api-improvements-in-wordpress-7-1/#custom-input-and-output-validation).

### Empty object defaults are represented as objects

In PHP, an empty array serializes to `[]`, even when its schema declares an object:

    ```notranslate
    array(
    	'type'    => 'object',
    	'default' => array(),
    )
    ```

For client-facing schemas, the empty default is prepared so that JSON serialization
produces an object:

    ```notranslate
    {
    	"type": "object",
    	"default": {}
    }
    ```

This prevents client validators from rejecting the default because its serialized
type does not match the declared `object` type.

## Allowed keywords filterFilter Filters are one of the two types of Hooks 󠀁[https://codex.wordpress.org/Plugin_API/Hooks](https://codex.wordpress.org/Plugin_API/Hooks)󠁿. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output.

`wp_prepare_json_schema_for_client()` uses `wp_get_json_schema_allowed_keywords()`
to decide which keywords to preserve.

The broader `draft-04` profile can preserve composition and documentation keywords
such as:

 * `$ref`
 * `definitions`
 * `allOf`
 * `not`
 * `dependencies`
 * `additionalItems`

Preserving a keyword means that it may be included in the client-facing schema. 
It does not mean that WordPress validates or sanitizes values against that keyword
on the server.

The allowed keyword list is filterable:

    ```notranslate
    add_filter(
    	'wp_json_schema_allowed_keywords',
    	function ( $keywords, $schema_profile ) {
    		if ( 'draft-04' === $schema_profile ) {
    			$keywords[] = 'x-example-keyword';
    		}

    		return $keywords;
    	},
    	10,
    	2
    );
    ```

Custom keywords should only be exposed when the receiving clients are known to understand
them.

## Abilities REST API changes

WordPress now prepares an ability’s input and output schemas before including them
in Abilities REST API responses.

For abilities registered with REST visibility, consumers of endpoints under:

    ```notranslate
    /wp-json/wp-abilities/v1/abilities
    ```

receive portable schemas instead of the original WordPress-internal schema arrays.

This is an output-boundary transformation:

 * `WP_Ability::get_input_schema()` and `WP_Ability::get_output_schema()` continue
   to return the original schemas to server-side PHP.
 * REST responses contain prepared versions.
 * Ability execution and server-side validation remain unchanged.

Developers comparing a schema retrieved directly from a `WP_Ability` object with
one returned through REST may therefore see intentional differences.

## AI Client integration

When the WordPress AI Client converts abilities into function declarations, it now
prepares each ability’s input schema first:

    ```notranslate
    $input_schema = wp_prepare_json_schema_for_client(
    	$ability->get_input_schema()
    );
    ```

This prevents WordPress-only schema keywords and nonportable `required` conventions
from being passed directly into AI function declarations.

The helper prepares a portable Draft 4 schema; it is not a provider-specific compiler.
Individual AI providers may support a smaller schema vocabulary or impose additional
requirements. Provider-specific adaptation may therefore still occur elsewhere in
the integration.

### Follow-up

Provider-specific adaptation is tracked in [WordPress/php-ai-client#256](https://github.com/WordPress/php-ai-client/issues/256):
each provider should be able to override the input schema for its own API requirements,
which differ across providers and evolve over time.

## Related ticket

 * [#64955](https://core.trac.wordpress.org/ticket/64955) — Add schema compiler 
   for AI tool calling compatibility
 * Changeset [[62591]](https://core.trac.wordpress.org/changeset/62591) — Abilities
   API: Reuse JSON Schema client preparation
 * Changeset [[62549]](https://core.trac.wordpress.org/changeset/62549) — REST API:
   Add a shared helper for JSON Schema allowed keywords.
 * Changeset [[62449]](https://core.trac.wordpress.org/changeset/62449) — Abilities
   API: Normalize `required` schema shape for REST responses 

_Props to [@jorbin](https://profiles.wordpress.org/jorbin/) for peer review and 
suggested improvements._

[#abilities-api](https://make.wordpress.org/core/tag/abilities-api/), [#7-1](https://make.wordpress.org/core/tag/7-1/),
[#dev-notes](https://make.wordpress.org/core/tag/dev-notes/), [#dev-notes-7-1](https://make.wordpress.org/core/tag/dev-notes-7-1/),
[#rest-api](https://make.wordpress.org/core/tag/rest-api/)

 * [Login to Reply](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fmake.wordpress.org%2Fcore%2F2026%2F07%2F31%2Fjson-schema-preparation-for-client-compatibility-in-wordpress-7-1%2F%23respond&locale=en_US)

 [  ](https://profiles.wordpress.org/milana_cap/) [Milana Cap](https://profiles.wordpress.org/milana_cap/)
7:07 pm _on_ July 31, 2026     
Tags: [#abilities-api ( 6 )](https://make.wordpress.org/core/tag/abilities-api/),
[7.1 ( 68 )](https://make.wordpress.org/core/tag/7-1/), [dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/),
[dev-notes-7-1 ( 28 )](https://make.wordpress.org/core/tag/dev-notes-7-1/), rest-
api   

# 󠀁[Abilities API improvements in WordPress 7.1](https://make.wordpress.org/core/2026/07/31/abilities-api-improvements-in-wordpress-7-1/)󠁿

WordPress 7.1 expands the Abilities APIAPI An API or Application Programming Interface
is a software intermediary that allows programs to interact with each other and 
share data in limited, clearly defined ways. with custom validation hooksHooks In
WordPress theme and development, hooks are functions that can be applied to an action
or a Filter in WordPress. Actions are functions performed when a certain event occurs
in WordPress. Filters allow you to modify certain functions. Arguments used to hook
both filters and actions look the same., a new invocation lifecycle action, richer
user information, selective field responses, and more consistent schemas for the
coreCore Core is the set of software required to run WordPress. The Core Development
Team builds WordPress. abilities introduced in WordPress 6.9.

 1. [Custom input and output validation](https://make.wordpress.org/core/2026/07/31/abilities-api-improvements-in-wordpress-7-1/#custom-input-and-output-validation)
 2. [Observing every ability invocation](https://make.wordpress.org/core/2026/07/31/abilities-api-improvements-in-wordpress-7-1/#observing-every-ability-invocation)
 3. [Expanded core/get-user-info responses](https://make.wordpress.org/core/2026/07/31/abilities-api-improvements-in-wordpress-7-1/#expanded-core-get-user-info-responses)
 4. [Consistent schemas across core abilities](https://make.wordpress.org/core/2026/07/31/abilities-api-improvements-in-wordpress-7-1/#consistent-schemas-across-core-abilities)
 5. [Typed input for REST ability runs](https://make.wordpress.org/core/2026/07/31/abilities-api-improvements-in-wordpress-7-1/#typed-input-for-rest-ability-runs)

## Custom input and output validation

`WP_Ability` validates input and output against each ability’s JSONJSON JSON, or
JavaScript Object Notation, is a minimal, readable format for structuring data. 
It is used primarily to transmit data between a server and web application, as an
alternative to XML. Schema. WordPress 7.1 adds two filters that let extenders supplement
that validation:

 * `wp_ability_validate_input`
 * `wp_ability_validate_output`

Each filterFilter Filters are one of the two types of Hooks [https://codex.wordpress.org/Plugin_API/Hooks](https://codex.wordpress.org/Plugin_API/Hooks).
They provide a way for functions to modify data of other functions. They are the
counterpart to Actions. Unlike Actions, filters are meant to work in an isolated
manner, and should never have side effects such as affecting global variables and
output. receives the existing validation result, the value being validated, and 
the ability name:

    ```notranslate
    /**
     * @param true|WP_Error $is_valid Validation result.
     * @param mixed         $value    Input or output value.
     * @param string        $name     Ability name.
     */
    ```

For example, a pluginPlugin A plugin is a piece of software containing a group of
functions that can be added to a WordPress website. They can extend functionality
or add new features to your WordPress websites. WordPress plugins are written in
the PHP programming language and integrate seamlessly with WordPress. These can 
be free in the WordPress.org Plugin Directory [https://wordpress.org/plugins/](https://wordpress.org/plugins/)
or can be cost-based plugin from a third-party. can enforce a rule that cannot be
expressed by the JSON Schema implementation used by WordPress:

    ```notranslate
    add_filter(
    	'wp_ability_validate_input',
    	function ( $is_valid, $input, $ability_name ) {
    		if ( 'my-plugin/send-message' !== $ability_name ) {
    			return $is_valid;
    		}

    		// Preserve errors produced by the default schema validation.
    		if ( is_wp_error( $is_valid ) ) {
    			return $is_valid;
    		}

    		if (
    			! is_array( $input )
    			|| empty( $input['recipient'] )
    			|| ! str_ends_with( $input['recipient'], '@example.com' )
    		) {
    			return new WP_Error(
    				'invalid_recipient',
    				__( 'The recipient must use the example.com domain.', 'my-plugin' )
    			);
    		}

    		return true;
    	},
    	10,
    	3
    );
    ```

Output can be validated in the same way:

    ```notranslate
    add_filter(
    	'wp_ability_validate_output',
    	function ( $is_valid, $output, $ability_name ) {
    		if ( 'my-plugin/send-message' !== $ability_name ) {
    			return $is_valid;
    		}

    		if ( is_wp_error( $is_valid ) ) {
    			return $is_valid;
    		}

    		if ( ! is_array( $output ) || empty( $output['message_id'] ) ) {
    			return new WP_Error(
    				'invalid_message_output',
    				__( 'The ability did not return a message ID.', 'my-plugin' )
    			);
    		}

    		return true;
    	},
    	10,
    	3
    );
    ```

Callbacks should return `true` when the value is valid or a `WP_Error` describing
why it is invalidinvalid A resolution on the bug tracker (and generally common in
software development, sometimes also _notabug_) that indicates the ticket is not
a bug, is a support request, or is generally invalid.. Returning `false` also fails
validation, but WordPress converts it to a generic `WP_Error`. The filters operate
on the validation result rather than the schema, allowing plugins to augment an 
existing error or reject data that otherwise passed JSON Schema validation.

REST-style `validate_callback` and `sanitize_callback` schema keywords are not executed
by the Abilities API. Custom runtime validation should use these new filters. See
ticketticket Created for both bug reports and feature development on the bug tracker.
[#64311](https://core.trac.wordpress.org/ticket/64311).

## Observing every ability invocation

WordPress 7.1 adds the `wp_ability_invoked` action at the beginning of `WP_Ability::
execute()`:

    ```notranslate
    do_action( 'wp_ability_invoked', $this->name, $input, $this );
    ```

The action fires before input normalization, validation, permission checks, and 
the [`wp_pre_execute_ability` short-circuit filter](https://make.wordpress.org/core/2026/07/29/new-execution-lifecycle-filters-for-the-abilities-api-in-wordpress-7-1/#short-circuiting-execution-with-wp-pre-execute-ability).
Consequently, it runs for every invocation, including calls that:

 * contain invalid input;
 * fail their permission check;
 * are short-circuited;
 * return a cached result;
 * require approval; or
 * proceed to the execution callback.

This makes the action suitable for auditing, telemetry, tracing, and invocation 
accounting:

    ```notranslate
    add_action(
    	'wp_ability_invoked',
    	function ( $ability_name, $input, $ability ) {
    		// Avoid storing sensitive input without appropriate filtering.
    		do_action(
    			'my_plugin_record_ability_invocation',
    			array(
    				'ability'  => $ability_name,
    				'timestamp' => time(),
    			)
    		);
    	},
    	10,
    	3
    );
    ```

The action receives raw, unnormalized input. Plugins should therefore avoid logging
input indiscriminately, because it may contain credentials, personal information,
or other sensitive data.

The existing `wp_before_execute_ability` and `wp_after_execute_ability` actions 
also receive the corresponding `WP_Ability` instance as an additional final argument.
Existing callbacks continue to work unchanged. To receive the new argument, update
the callback signature and `$accepted_args`. See ticket [#65248](https://core.trac.wordpress.org/ticket/65248).

## Expanded `core/get-user-info` responses

The `core/get-user-info` ability now returns five additional profile fields for 
the current authenticated user:

 * `first_name`
 * `last_name`
 * `nickname`
 * `description`
 * `user_url`

When no input is supplied, the response contains all supported properties:

    ```notranslate
    {
    	"id": 1,
    	"display_name": "Jane Doe",
    	"user_nicename": "jane-doe",
    	"user_login": "jane",
    	"roles": [ "administrator" ],
    	"locale": "en_US",
    	"first_name": "Jane",
    	"last_name": "Doe",
    	"nickname": "Jane",
    	"description": "Site administrator and contributor.",
    	"user_url": "https://example.com"
    }
    ```

The `roles` property is now normalized with `array_values()` so that it is consistently
encoded as a JSON array, regardless of its PHPPHP The web scripting language in 
which WordPress is primarily architected. WordPress requires PHP 7.4 or higher array
keys.

Callers can also use the new optional `fields` input property to request a subset
of the response:

    ```notranslate
    $ability = wp_get_ability( 'core/get-user-info' );

    $result = $ability->execute(
    	array(
    		'fields' => array(
    			'display_name',
    			'first_name',
    			'last_name',
    		),
    	)
    );
    ```

The resulting value contains only the requested properties:

    ```notranslate
    {
    	"display_name": "Jane Doe",
    	"first_name": "Jane",
    	"last_name": "Doe"
    }
    ```

Supported field names are declared through an `enum` in the input schema. Passing
an unknown name causes execution to return an `ability_invalid_input` error before
the ability callback runs.

The permission requirement remains unchanged: the requester must be logged in. See
ticket [#65234](https://core.trac.wordpress.org/ticket/65234) and changeset [[62419]](https://core.trac.wordpress.org/changeset/62419).

## Consistent schemas across core abilities

The following core abilities now follow the same schema conventions:

 * `core/get-site-info`
 * `core/get-user-info`
 * `core/get-environment-info`

Every output property declares a translatable, Title Case `title` and a `description`.
This provides better metadata to REST, MCP, WebMCP, AI, and other programmatic clients
that use the schema to present or select ability fields.

`core/get-environment-info` now supports the same optional `fields` input as the
other two abilities:

    ```notranslate
    $ability = wp_get_ability( 'core/get-environment-info' );

    $result = $ability->execute(
    	array(
    		'fields' => array( 'php_version' ),
    	)
    );
    ```

Unknown field names are rejected through schema validation.

In addition, `core/get-user-info` is now exposed through the REST APIREST API The
REST API is an acronym for the RESTful Application Program Interface (API) that 
uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of
an application (think “phone app” or “website”) can communicate with the data store(
think “database” or “file system”) [https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/).
Like the other core abilities, it declares the new `public` metaMeta Meta is a term
that refers to the inside workings of a group. For us, this is the team that works
on internal WordPress sites like WordCamp Central and Make WordPress. flag introduced
in WordPress 7.1. Authenticated clients can discover it through:

    ```notranslate
    /wp-json/wp-abilities/v1/abilities
    ```

The exact ordered set of input field names and output properties is covered by registration
tests. Developers extending or consuming these core abilities should use the schemas
for discovery rather than assuming a fixed response shape. See ticket [#65355](https://core.trac.wordpress.org/ticket/65355).

## Typed input for REST ability runs

REST requests that run an ability over `GET` or `DELETE` deliver every query string
value as a string, and a comma-separated list as a single string. In previous releases,
the ability received this input as-is: an integer arrived as `"10"`, a boolean as`"
true"`, and strict comparisons inside the callback silently failed unless the ability
hand-rolled its own casting.

WordPress 7.1 coerces the input of a run request to the types declared in the ability’s`
input_schema` before the ability runs. The coercion is registered as the `input`
argument’s `sanitize_callback`, so the permission callback and the execute callback
both receive natively typed input from the same request object:

    ```notranslate
    GET /wp-json/wp-abilities/v1/abilities/my-plugin/list-items/run
        ?input[limit]=10&input[featured]=true&input[ids]=1,2,3
    ```

With an input schema declaring `limit` as an integer, `featured` as a boolean, and`
ids` as an array of integers, the callbacks now receive:

    ```notranslate
    array(
    	'limit'    => 10,
    	'featured' => true,
    	'ids'      => array( 1, 2, 3 ),
    )
    ```

Coercion never changes what validation accepts. Input is coerced only when `validate_input()`
already accepts it, so invalid input reaches validation untouched and returns the
same `ability_invalid_input` error as before.

_Props to [@benjamin\_zekavica](https://profiles.wordpress.org/benjamin_zekavica/)
and [@annezazu](https://profiles.wordpress.org/annezazu/) for peer review, and to
[@jorgefilipecosta](https://profiles.wordpress.org/jorgefilipecosta/) for review
and collaboration._

[#abilities-api](https://make.wordpress.org/core/tag/abilities-api/), [#7-1](https://make.wordpress.org/core/tag/7-1/),
[#dev-notes](https://make.wordpress.org/core/tag/dev-notes/), [#dev-notes-7-1](https://make.wordpress.org/core/tag/dev-notes-7-1/),
[#rest-api](https://make.wordpress.org/core/tag/rest-api/)

 * [Login to Reply](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fmake.wordpress.org%2Fcore%2F2026%2F07%2F31%2Fabilities-api-improvements-in-wordpress-7-1%2F%23respond&locale=en_US)

 [  ](https://profiles.wordpress.org/kadamwhite/) [K. Adam White](https://profiles.wordpress.org/kadamwhite/)
4:28 pm _on_ October 11, 2022     
Tags: [core-restapi ( 34 )](https://make.wordpress.org/core/tag/core-restapi/),
[dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/), [dev-notes-6.1 ( 37 )](https://make.wordpress.org/core/tag/dev-notes-6-1/),
rest-api   

# 󠀁[Miscellaneous REST API improvements in WordPress 6.1](https://make.wordpress.org/core/2022/10/11/miscellaneous-rest-api-improvements-in-wordpress-6-1/)󠁿

## Search REST resources by ID across subtypes

WordPress 6.1 includes an enhancementenhancement Enhancements are simple improvements
to WordPress, such as the addition of a hook, a new feature, or an improvement to
an existing feature. to the search controller, [#56546](https://core.trac.wordpress.org/ticket/56546),
which makes it possible to retrieve a term or post object over the REST APIREST 
API The REST API is an acronym for the RESTful Application Program Interface (API)
that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end
of an application (think “phone app” or “website”) can communicate with the data
store (think “database” or “file system”) [https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/)
without knowing anything but that resource’s ID and object type.

`get_post` can retrieve a post of any post type so long as you know the post’s numeric
ID, and `get_term` can retrieve a term from any taxonomyTaxonomy A taxonomy is a
way to group things together. In WordPress, some common taxonomies are category,
link, tag, or post format. [https://codex.wordpress.org/Taxonomies#Default_Taxonomies](https://codex.wordpress.org/Taxonomies#Default_Taxonomies)..
Because REST objects are segregated by post type-specific endpoints, however, there
has not been a clear way to get a Post with ID 78 if you don’t know whether it is
a `page`, `post`, or `my-cpt`.

The coreCore Core is the set of software required to run WordPress. The Core Development
Team builds WordPress. `/search` endpoint now supports `?include` and `?exclude`
parameters which take a list of IDs, and limit results to posts matching those IDs.

Examples:

 * To get post 78 when you don’t know its post type,
    - `/wp/v2/search?include=78`
 * To get posts 78 and 79 only if they are in the `page` post type,
    - `/wp/v2/search?include=78,79&subtype=page`
 * To search posts excluding post 78,
    - `/wp/v2/search?exclude=78`
 * To get term 87,
    - `/wp/v2/search?type=term&include=78`
 * To get term 87 only if it is a categoryCategory The 'category' taxonomy lets 
   you group posts / content together that share a common bond. Categories are pre-
   defined and broad ranging.,
    - `/wp/v2/search?type=term&include=78&subtype=category`
 * To search terms excluding terms 87 and 88,
    - `/wp/v2/search?exclude=87,88`

The search endpoint supports the `_embed` metaMeta Meta is a term that refers to
the inside workings of a group. For us, this is the team that works on internal 
WordPress sites like WordCamp Central and Make WordPress. parameter, so developers
can therefore use the search endpoint to retrieve a full post or term response object
in one request knowing only those object’s IDs.

As an example of how this could be used, imagine a custom blockBlock Block is the
abstract term used to describe units of markup that, composed together, form the
content or layout of a webpage using the WordPress editor. The idea combines concepts
of what in the past may have achieved with shortcodes, custom HTML, and embed discovery
into a single consistent API and user experience. which relates to a specific post.
As of WordPress 6.1 developers can implement that block knowing only the related
post’s ID, and could then create a hook to search for that post by ID and retrieve
it using the Block Editor’s existing entity system:

    ```notranslate
    /**
     * Dictionary of requested items: keep an in-memory list of the type (if known)
     * for each requested ID, to limit unnecessary API requests.
     */
    const typeById = {};
    ​
    /**
     * Query for a post entity resource without knowing its post type.
     *
     * @param {number} id Numeric ID of a post resource of unknown subtype.
     * @returns {object|undefined} The requested post object, if found and loaded.
     */
    function usePostById( id ) {
        const type = typeById[ id ];
    ​
        useEffect( function() {
            if ( ! id || typeById[ id ] ) {
                return;
            }
    ​
            apiFetch( {
                path: `/wp/v2/search?type=post&include=${ id }&_fields=id,subtype`,
            } ).then( ( result ) => {
                if ( result.length ) {
                    typeById[ id ] = result[0].subtype;
                }
            } );
        }, [ id ] );
    ​
        return useSelect( function( select ) {
            if ( ! id || ! type ) {
                return undefined;
            }
            return select( 'core' ).getEntityRecord( 'postType', type, id );
        }, [ id, type ] );
    }
    ```

## Pretty-printing REST endpoint JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. responses

WordPress 6.1 also introduces support for returning pre-formatted JSON from the 
REST API. [#41998](https://core.trac.wordpress.org/ticket/41998) lets developers
request formatted JSON using a new `_pretty` query parameter or a filterFilter Filters
are one of the two types of Hooks [https://codex.wordpress.org/Plugin_API/Hooks](https://codex.wordpress.org/Plugin_API/Hooks).
They provide a way for functions to modify data of other functions. They are the
counterpart to Actions. Unlike Actions, filters are meant to work in an isolated
manner, and should never have side effects such as affecting global variables and
output., particularly useful when querying via curl or other tools which do not 
provide an option to format responses.

To format the JSON returned from a specific endpoint request, append the `?_pretty`
query parameter to the endpoint URLURL A specific web address of a website or web
page on the Internet, such as a website’s URL www.wordpress.org.

To instruct WordPress to pretty-print all REST response bodies, a developer can 
use the `rest_json_encode_options` filter:

    ```notranslate
    function myproject_pretty_print_rest_responses( $options ) {
            $options |= JSON_PRETTY_PRINT;
    ​
            return $options;
    }
    add_filter( 'rest_json_encode_options', 'myproject_pretty_print_rest_responses', 10 );
    ```

A developer or site owner may also disable pretty-printing globally using the same
filter:

    ```notranslate
    function myproject_disable_rest_pretty_printing( $options ) {
            $options &= ~JSON_PRETTY_PRINT;
    ​
            return $options;
    }
    add_filter( 'rest_json_encode_options', 'myproject_disable_rest_pretty_printing', 10 );
    ```

Filters are applied after, and can override, the `_pretty` query parameter.

_Thanks to _[@spacedmonkey](https://profiles.wordpress.org/spacedmonkey/)_ for peer
review._

[#core-restapi](https://make.wordpress.org/core/tag/core-restapi/), [#dev-notes](https://make.wordpress.org/core/tag/dev-notes/),
[#dev-notes-6-1](https://make.wordpress.org/core/tag/dev-notes-6-1/), [#rest-api](https://make.wordpress.org/core/tag/rest-api/)

 [  ](https://profiles.wordpress.org/spacedmonkey/) [Jonny Harris](https://profiles.wordpress.org/spacedmonkey/)
2:26 pm _on_ October 10, 2022     
Tags: [6.1 ( 94 )](https://make.wordpress.org/core/tag/6-1/),
[core-restapi ( 34 )](https://make.wordpress.org/core/tag/core-restapi/), [dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/),
[dev-notes-6.1 ( 37 )](https://make.wordpress.org/core/tag/dev-notes-6-1/), [performance ( 419 )](https://make.wordpress.org/core/tag/performance/),
rest-api   

# 󠀁[Performance improvements to the REST API](https://make.wordpress.org/core/2022/10/10/performance-improvements-to-the-rest-api/)󠁿

WordPress 6.1 brings a number of key improvements to the REST APIREST API The REST
API is an acronym for the RESTful Application Program Interface (API) that uses 
HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application(
think “phone app” or “website”) can communicate with the data store (think “database”
or “file system”) [https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/)
to increase performance. These improvements decrease the number of database queries
that are run on each REST API request. 

## Avoid unnecessarily preparing item links

Prior to WordPress 6.1, the `prepare_links` method in the REST API was called in
all controllers. If the `_fields` parameter is passed to the REST API request, it
might mean that the links field is not requested and would never be returned in 
the response. This is wasteful, as `prepare_links` can contain database calls or
other complex logic that would be run even if it never returned the response. 

In 6.1 `prepare_links` are only called if requested in the response, when links 
are requested in fields or the `_embedded` parameter is passed. As part of this 
work, the taxonomyTaxonomy A taxonomy is a way to group things together. In WordPress,
some common taxonomies are category, link, tag, or post format. [https://codex.wordpress.org/Taxonomies#Default_Taxonomies](https://codex.wordpress.org/Taxonomies#Default_Taxonomies).
and post type controllers have now been updated to implement said `prepare_links`
method to bring them in line with other REST API controllers. 

This is the code example of implementing this change in custom REST API controllers.

**Before**

    ```notranslate
    $response = rest_ensure_response( $data );
    $links = $this->prepare_links( $post );
    $response->add_links( $links );

    return $response;
    ```

**After**

    ```notranslate
    $response = rest_ensure_response( $data );

    if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
      $links = $this->prepare_links( $post );
      $response->add_links( $links );
    }

    return $response;
    ```

This logic conditionally calls prepare_links _only_ if `_links` or `_embedded` is
requested. This logic only applies when using the `_fields` query parameter. When
not using `_fields`, links are included in the response as normal. 

For more info see TracTrac An open source project by Edgewall Software that serves
as a bug tracker and project management tool for WordPress. ticketticket Created
for both bug reports and feature development on the bug tracker.: [#52992](https://core.trac.wordpress.org/ticket/52992),
[#56019](https://core.trac.wordpress.org/ticket/56019), [#56020](https://core.trac.wordpress.org/ticket/56020)

## Improvement to the Posts controller

When running profiling tools against the responses of REST API requests, it was 
discovered that post controllers request a lot of linked data to each post. For 
example, when returning a post in a REST API response, linked data such as author(
user), featured imageFeatured image A featured image is the main image used on your
blog archive page and is pulled when the post or page is shared on social media.
The image can be used to display in widget areas on your site or in a summary list
of posts., and parent post were all requested. As these linked items were not primed
in caches, it could mean that for each post in the REST API response there would
be 3 separate database queries: one for the user, one for the featured image, and
another for the parent post. 

In WordPress 6.1 all the caches are primed in a single database query and there 
are new helper functions to enable this:

`update_post_author_caches`

Takes an array of posts and primes users caches in a single query. 

`update_post_parent_caches`

Takes an array of posts and primes post parents in a single query. 

`update_menu_item_cache`

Takes an array of posts and primes post / terms link to menu items single query.

The existing function `update_post_thumbnail_cache` was used to prime featured image
caches. These functions are also being rolled out to other parts of the coreCore
Core is the set of software required to run WordPress. The Core Development Team
builds WordPress. that can benefit from priming caches in a single place. 

For more info see Trac tickets: [#55592](https://core.trac.wordpress.org/ticket/55592),
[#55593](https://core.trac.wordpress.org/ticket/55593), [#55620](https://core.trac.wordpress.org/ticket/55620)

## Improvements to other controllers

The comments and user controllers have also been improved: User controller now primes
user metaMeta Meta is a term that refers to the inside workings of a group. For 
us, this is the team that works on internal WordPress sites like WordCamp Central
and Make WordPress. in a single query and the comments controller now primes the
linked post cache in a single query. Improvements were made to the post search controller
to improve database performance along with the media controller. 

For more info see Trac tickets: [#55674](https://core.trac.wordpress.org/ticket/55674),
[#56272](https://core.trac.wordpress.org/ticket/56272), [#55677](https://core.trac.wordpress.org/ticket/55677),
[#55716](https://core.trac.wordpress.org/ticket/55716)

_Props to _[@flixos90](https://profiles.wordpress.org/flixos90/)_ and _[@milana_cap](https://profiles.wordpress.org/milana_cap/)_
for peer review and [@mxbclang](https://profiles.wordpress.org/mxbclang/) and [@webcommsat](https://profiles.wordpress.org/webcommsat/)
for proofreading. _

[#6-1](https://make.wordpress.org/core/tag/6-1/), [#core-restapi](https://make.wordpress.org/core/tag/core-restapi/),
[#dev-notes](https://make.wordpress.org/core/tag/dev-notes/), [#dev-notes-6-1](https://make.wordpress.org/core/tag/dev-notes-6-1/),
[#performance](https://make.wordpress.org/core/tag/performance/), [#rest-api](https://make.wordpress.org/core/tag/rest-api/)

 [  ](https://profiles.wordpress.org/spacedmonkey/) [Jonny Harris](https://profiles.wordpress.org/spacedmonkey/)
11:37 am _on_ January 24, 2022     
Tags: [5.9 ( 104 )](https://make.wordpress.org/core/tag/5-9/),
[dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/), rest-api   

# 󠀁[Menus endpoints in WordPress 5.9](https://make.wordpress.org/core/2022/01/24/menus-endpoints-in-wordpress-5-9/)󠁿

WordPress 5.9 adds three new REST APIREST API The REST API is an acronym for the
RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT,
POST and DELETE data. It is how the front end of an application (think “phone app”
or “website”) can communicate with the data store (think “database” or “file system”)
[https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/)
endpoints to manage menus and menu locations. These menus endpoints are used the
in new [navigation block](https://make.wordpress.org/core/2022/01/07/the-new-navigation-block/).

> [The new Navigation block](https://make.wordpress.org/core/2022/01/07/the-new-navigation-block/)

Before discussing menu endpoints, it’s worth noting how menus are currently stored.
Navigation menus are stored using the `nav_menu` taxonomyTaxonomy A taxonomy is 
a way to group things together. In WordPress, some common taxonomies are category,
link, tag, or post format. [https://codex.wordpress.org/Taxonomies#Default_Taxonomies](https://codex.wordpress.org/Taxonomies#Default_Taxonomies).
and the `nav_menu_item` post type. A menu is stored as a term and acts like a container
for a number of menu items. Menu items are stored as posts. Menus and menu items
also have custom fields stored in metaMeta Meta is a term that refers to the inside
workings of a group. For us, this is the team that works on internal WordPress sites
like WordCamp Central and Make WordPress. and in various options.

### Menus

Accessible via `/wp/v2/menus`, the menus endpoint allows for performing CRUDCRUD
Create, read, update and delete, the four basic functions of storing data. (More
on [Wikipedia](http://en.wikipedia.org/wiki/Create,_read,_update_and_delete).) operations
on menu data. This endpoint extends the `WP_REST_Terms_Controller` class, mapping
fields to the menus and adding functionality like `auto_add`, that automatically
adds new pages to menus when created. A `GET` request includes the list of menus,
but does not contain the list of menu items. To get this data, the menu items endpoint
can be used.

Example output from request:

    ```notranslate
    {
        "auto_add": false
        "description": "",
        "id": 36,
        "locations": ['wporg_header_subsite_nav', 'primary'],
        "meta": [],
        "name": "Navigation",
        "slug": "navigation",
    }
    ```

### Menu items

Accessible via `/wp/v2/menu-items`, the menu items endpoint allows for performing
CRUD operations on menu items and assigning them to menus. This endpoint extends
the `WP_REST_Posts_Controller` class, mapping fields and adding custom functionality.
Menu items can only be assigned to one menu at a time unlike other taxonomies.

Many menu items have an associated object that the menu item links to. For instance,
a link to a page will have the `object` set to `page` and the `object_id` set to
the WordPress Post ID of that page. When using this endpoint, it may be useful to
get information about that linked object. For example the page’s title. This information
is not included in the response by default, but the REST API has a feature to embed
this information by using the `_embed` query parameter. For example, making a `GET`
request to `/wp/v2/menu-item/8874?_embed=true` will result in the following response:

    ```notranslate
    {
         "attr_title": "",
         "classes": [''],
         "description": "",
         "id": 8874,
         "invalid": false,
         "menu_order": 1,
         "menus": 363,
         "meta": {},
         "object": "page",
         "object_id": 8823,
         "parent": 0,
         "status": "publish",
         "target": "",
         "title": {rendered: 'Tickets'},
         "type": "post_type",
         "type_label": "Page",
         "url": "https://make.wordpress.org/core/reports/",
         "xfn": [""],
         "_embedded": {
                  wp:menu-item-object: [
                       {
                          "author": 5286302,
                          "date": "2014-01-29T19:49:26",
                          "featured_media": 0,
                          "id": 8823,
                          "link": "https://make.wordpress.org/core/reports/",
                          "slug": "reports",
                          "title": { "rendered": "Bug Reports"},
                          "type": "page",
                       }
                  ],
                  wp:term: [
                        {
                           "id": 363,
                           "name": "Navigation",
                           "slug": "navigation"
                        }
                  ]
        },
    }
    ```

### Menu Locations

Accessible via `/wp/v2/menu-locations`, the menu locations endpoint returns a list
of menu locations registered with the [`register_nav_menus`](https://developer.wordpress.org/themes/functionality/navigation-menus/)
function. To assign a menu to a particular location, use the menus endpoint, by 
passing an array of menu location keys.

Example output from request:

    ```notranslate
    {
         "description": "Desktop Horizontal Menu"
         "menu": 90
         "name": "primary"
    }
    ```

### Batching requests

Both the menu and menu item endpoints, both support the batching of requests, [introduced in WordPress 5.6.](https://make.wordpress.org/core/2020/11/20/rest-api-batch-framework-in-wordpress-5-6/)
This means that more than one menu / menu item can be updates / created in a simple
request to the APIAPI An API or Application Programming Interface is a software 
intermediary that allows programs to interact with each other and share data in 
limited, clearly defined ways..

### Access Control

To access data from any of the menus endpoints, requests must be made by a logged
in user with the `edit_theme_options` capabilitycapability A **capability** is permission
to perform one or more types of task. Checking if a user has a capability is performed
by the `current_user_can` function. Each user of a WordPress site might have some
permissions but not others, depending on their role. For example, users who have
the Author role usually have permission to edit their own posts (the “edit_posts”
capability), but not permission to edit other users’ posts (the “edit_others_posts”
capability).. By default, only users with the `Administrator` role have this capability.
This means that menu data is not publically exposed via the REST API. Ticketticket
Created for both bug reports and feature development on the bug tracker. [#54304](https://core.trac.wordpress.org/ticket/54304)
provides a means for developers to opt-in to exposing this data publicly. The REST
API team hopes to implement this feature in a near future release of WordPress.

These endpoints were first developed as a feature pluginFeature Plugin A plugin 
that was created with the intention of eventually being proposed for inclusion in
WordPress Core. See [Features as Plugins](https://make.wordpress.org/core/handbook/about/release-cycle/features-as-plugins/)
on [GitHub](https://github.com/WP-API/menus-endpoints). For those using this pluginPlugin
A plugin is a piece of software containing a group of functions that can be added
to a WordPress website. They can extend functionality or add new features to your
WordPress websites. WordPress plugins are written in the PHP programming language
and integrate seamlessly with WordPress. These can be free in the WordPress.org 
Plugin Directory [https://wordpress.org/plugins/](https://wordpress.org/plugins/)
or can be cost-based plugin from a third-party., after upgrading to WordPress 5.9,
the plugin can be deactivated and removed as all of it’s functionality is now included
in WordPress CoreCore Core is the set of software required to run WordPress. The
Core Development Team builds WordPress..

_Props to _[@timothyblynjacobs](https://profiles.wordpress.org/timothyblynjacobs/)
_for peer review_.

[#5-9](https://make.wordpress.org/core/tag/5-9/), [#dev-notes](https://make.wordpress.org/core/tag/dev-notes/),
[#rest-api](https://make.wordpress.org/core/tag/rest-api/)

 [  ](https://profiles.wordpress.org/timothyblynjacobs/) [Timothy Jacobs](https://profiles.wordpress.org/timothyblynjacobs/)
4:59 am _on_ June 29, 2021     
Tags: [5.8 ( 99 )](https://make.wordpress.org/core/tag/5-8/),
[dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/), rest-api   

# 󠀁[REST API Changes in WordPress 5.8](https://make.wordpress.org/core/2021/06/29/rest-api-changes-in-wordpress-5-8/)󠁿

The following is a snapshot of some of the changes to the REST APIREST API The REST
API is an acronym for the RESTful Application Program Interface (API) that uses 
HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application(
think “phone app” or “website”) can communicate with the data store (think “database”
or “file system”) [https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/)
in WordPress 5.8. For more details, see the full list of [closed tickets](https://core.trac.wordpress.org/query?component=REST+API&milestone=5.8&or&focuses=~rest-api&milestone=5.8&or&component=Application+Passwords&milestone=5.8&col=id&col=summary&col=owner&col=type&col=status&col=priority&col=version&order=priority).

## Widgets

WordPress 5.8 sees the introduction of a new blockBlock Block is the abstract term
used to describe units of markup that, composed together, form the content or layout
of a webpage using the WordPress editor. The idea combines concepts of what in the
past may have achieved with shortcodes, custom HTML, and embed discovery into a 
single consistent API and user experience.-based widgets editor and with it the 
creation of several REST API endpoints dedicated to widgetWidget A WordPress Widget
is a small block that performs a specific function. You can add these widgets in
sidebars also known as widget-ready areas on your web page. WordPress widgets were
originally created to provide a simple and easy-to-use way of giving design and 
structure control of the WordPress theme to the user. management. Before diving 
in to how the new endpoints operate, I’d like to provide some background about how
widgets work that should make the following sections more clear.

#### Instance Widgets

The predominant way to create widgets is to subclass the `WP_Widget` base class 
and register the widget with `register_widget`. These are referred to as “multi”
widgets. These widgets have multiple instances that are identified by their `number`,
an incrementing integer for each widget type.

Each instance has its own setting values. These are stored and fetched by `WP_Widget`
which allows for the REST API to include these values. However, since a widget’s
instance can contain arbitrary data, for example a `DateTime` object, the REST API
cannot always serialize a widget to JSONJSON JSON, or JavaScript Object Notation,
is a minimal, readable format for structuring data. It is used primarily to transmit
data between a server and web application, as an alternative to XML.. As such, a
widget’s data is always serialized using the PHPPHP The web scripting language in
which WordPress is primarily architected. WordPress requires PHP 7.4 or higher `
serialize` function and then base64 encoded. This data is also exposed with a `hash`
value which is a `wp_hash` signature of this value to prevent clients from sending
arbitrary data to be deserialized with `unserialize`.

For widgets that can be safely accept and expose their instance data as JSON, pass
the `show_instance_in_rest` flag in the `$widget_options` parameter.

    ```notranslate
    class ExampleWidget extends WP_Widget {
        ...
        /**
         * Sets up the widget
         */
        public function __construct() {
            $widget_ops = array(
                // ...other options here
                'show_instance_in_rest' => true,
                // ...other options here
            );
            parent::__construct( 'example_widget', 'ExampleWidget', $widget_ops );
        }
        ...
    }
    ```

#### Reference Widgets

Far less common, but still supported, are widgets that are registered using `wp_register_sidebar_widget`
and `wp_register_widget_control` directly. These are referred to as “reference”,“
function-based”, or “non-multi” widgets. These widgets can store their data in an
arbitrary location. As such, their instance values are never included in the REST
API.

### Widget Types

Accessible via `/wp/v2/widget-types`, the widget types endpoint describes the different
widget types that are registered on the server. The endpoint is accessible to users
who have permission to `edit_theme_options`. By default, this is limited to Administrator
users.

#### Response Format

    ```notranslate
    {
      "id": "pages",
      "name": "Pages",
      "description": "A list of your site’s Pages.",
      "is_multi": true,
      "classname": "widget_pages",
      "_links": {
        "collection": [
          {
            "href": "https://trunk.test/wp-json/wp/v2/widget-types"
          }
        ],
        "self": [
          {
            "href": "https://trunk.test/wp-json/wp/v2/widget-types/pages"
          }
        ]
      }
    }
    ```

### Encode Endpoint

Multi widgets have access to the `/wp/v2/widget-types/<widget>/encode` endpoint.
This endpoint is used to convert htmlHTML HyperText Markup Language. The semantic
scripting language primarily used for outputting content in web browsers. form data
for the widget to the next `instance` for the widget, render the widget form, and
render the widget preview.

For example, let’s say we want to interact with the MetaMeta Meta is a term that
refers to the inside workings of a group. For us, this is the team that works on
internal WordPress sites like WordCamp Central and Make WordPress. widget. First,
we’ll want to request the widget form from the server.

    ```wp-block-code
    POST /wp/v2/widget-types/meta/encode
    ```

    ```notranslate
    {
      "instance": {},
      "number": 1
    }
    ```

For now, let’s assume we’re working with a new widget. The `instance` is empty because
this is a new widget, so we’ll be rendering an empty form. The `number` argument
can be omitted, but including one is recommended to provide stable input ids. You’ll
receive a response similar to this. _The widget preview has been snipped for brevity._

    ```notranslate
    {
      "form": "<p><label for=\"widget-meta-1-title\">Title:</label><input class=\"widefat\" id=\"widget-meta-1-title\" name=\"widget-meta[1][title]\" type=\"text\" value=\"\" /></p>",
      "preview": "<div class=\"widget widget_meta\">...</div>",
      "instance": {
        "encoded": "YToxOntzOjU6InRpdGxlIjtzOjA6IiI7fQ==",
        "hash": "77e9f20acb54fa4f77de5a865333c0e6",
        "raw": {
          "title": ""
        }
      }
    }
    ```

The provided form can then be rendered and edited by the user. When you want to 
render a new preview or are ready to begin saving, call the `encode` endpoint again
with the url encoded form data and the `instance` value returned from the first 
response.

    ```wp-block-code
    POST /wp/v2/widget-types/meta/encode
    ```

    ```notranslate
    {
      "instance": {
        "encoded": "YToxOntzOjU6InRpdGxlIjtzOjA6IiI7fQ==",
        "hash": "77e9f20acb54fa4f77de5a865333c0e6",
        "raw": {
          "title": ""
        }
      },
      "number": 1,
      "form_data": "widget-meta%5B1%5D%5Btitle%5D=Site+Info"
    }
    ```

The REST API will call the widget’s update function to calculate the new instance
based on the provided form data. The `instance` object can then be used to save 
a widget via the widgets endpoint.

    ```notranslate
    {
      "form": "<p><label for=\"widget-meta-1-title\">Title:</label><input class=\"widefat\" id=\"widget-meta-1-title\" name=\"widget-meta[1][title]\" type=\"text\" value=\"Site Info\" /></p>",
      "preview": "<div class=\"widget widget_meta\">...</div>",
      "instance": {
        "encoded": "YToxOntzOjU6InRpdGxlIjtzOjk6IlNpdGUgSW5mbyI7fQ==",
        "hash": "0e9a5bff2d28cba322c8cd27cd4e77af",
        "raw": {
          "title": "Site Info"
        }
      }
    }
    ```

### Widgets Endpoint

The widgets endpoint is used for performing CRUDCRUD Create, read, update and delete,
the four basic functions of storing data. (More on [Wikipedia](http://en.wikipedia.org/wiki/Create,_read,_update_and_delete).)
operations on the saved widgets. Like the widget types endpoint, the widgets endpoints
required the `edit_theme_options` capabilitycapability A **capability** is permission
to perform one or more types of task. Checking if a user has a capability is performed
by the `current_user_can` function. Each user of a WordPress site might have some
permissions but not others, depending on their role. For example, users who have
the Author role usually have permission to edit their own posts (the “edit_posts”
capability), but not permission to edit other users’ posts (the “edit_others_posts”
capability). to access.

 To retrieve widgets, make a `GET` request to the `/wp/v2/widgets` endpoint. The`
sidebar` parameter can be used to limit the response to widgets belonging to the
requested sidebarSidebar A sidebar in WordPress is referred to a widget-ready area
used by WordPress themes to display information that is not a part of the main content.
It is not always a vertical column on the side. It can be a horizontal rectangle
below or above the content area, footer, header, or any where in the theme..

To create a widget, for instance the widget from our previous example, make a `POST`
request to the `/wp/v2/widgets` endpoint. The `instance` is the same value returned
from the `encode` endpoint. The `id_base` is the unique identifier for the widget
type and `sidebar` is the id of the sidebar to assign the widget to. Both are required.

    ```wp-block-code
    POST /wp/v2/widgets
    ```

    ```notranslate
    {
    	"instance": {
    		"encoded": "YToxOntzOjU6InRpdGxlIjtzOjk6IlNpdGUgSW5mbyI7fQ==",
    		"hash": "0e9a5bff2d28cba322c8cd27cd4e77af",
    		"raw": {
    			"title": "Site Info"
    		}
    	},
    	"sidebar": "sidebar-1",
    	"id_base": "meta"
    }
    ```

The endpoint will return information about the newly created widget.

    ```notranslate
    {
      "id": "meta-1",
      "id_base": "meta",
      "sidebar": "sidebar-1",
      "rendered": "<div class=\"widget widget_meta\">...</div>",
      "rendered_form": "<p><label for=\"widget-meta-1-title\">Title:</label><input class=\"widefat\" id=\"widget-meta-1-title\" name=\"widget-meta[1][title]\" type=\"text\" value=\"Site Info\" /></p>",
      "instance": {
        "encoded": "YToxOntzOjU6InRpdGxlIjtzOjk6IlNpdGUgTWV0YSI7fQ==",
        "hash": "dac44c3ebfa0428fed61829fa151e4e8",
        "raw": {
          "title": "Site Info"
        }
      },
      "_links": {
        "self": [
          {
            "href": "https://trunk.test/wp-json/wp/v2/widgets/meta-1"
          }
        ],
        "collection": [
          {
            "href": "https://trunk.test/wp-json/wp/v2/widgets"
          }
        ],
        "about": [
          {
            "embeddable": true,
            "href": "https://trunk.test/wp-json/wp/v2/widget-types/meta"
          }
        ],
        "wp:sidebar": [
          {
            "href": "https://trunk.test/wp-json/wp/v2/sidebars/sidebar-1/"
          }
        ],
        "curies": [
          {
            "name": "wp",
            "href": "https://api.w.org/{rel}",
            "templated": true
          }
        ]
      }
    }
    ```

Since the meta widget (and all other built-in widgets) is registered with `show_instance_in_rest`
enabled you could bypass the `encode` endpoint and use `instance.raw` instead. For
example, if we wanted to update the widget to have a new title, we could make the
following `PUT` request to `/wp/v2/widgets/meta-1`.

    ```wp-block-code
    PUT /wp/v2/widgets/meta-1
    ```

    ```notranslate
    {
    	"instance": {
    		"raw": {
    			"title": "Site Meta"
    		}
    	}
    }
    ```

A `PUT` request can also be made to update the sidebar assigned to a widget by passing
a new `sidebar` id in the request.

To delete a widget, send a `DELETE` request to the individual widget route. By default,
deleting a widget will move a widget to the Inactive Widgets area. To permanently
delete a widget, use the `force` parameter. For example: `DELETE /wp/v2/widgets/
meta-1?force=true`.

### Sidebars Endpoints

Found under `/wp/v2/sidebars`, the sidebars endpoint is used to manage a site’s 
registered sidebars (widget areas) and their assigned widgets. For example, the 
following is the response for the first footer area in the Twenty Twenty theme.

    ```notranslate
    {
      "id": "sidebar-1",
      "name": "Footer #1",
      "description": "Widgets in this area will be displayed in the first column in the footer.",
      "class": "",
      "before_widget": "<div class=\"widget %2$s\"><div class=\"widget-content\">",
      "after_widget": "</div></div>",
      "before_title": "<h2 class=\"widget-title subheading heading-size-3\">",
      "after_title": "</h2>",
      "status": "active",
      "widgets": [
        "recent-posts-2",
        "recent-comments-2",
        "meta-1"
      ],
      "_links": {
        "collection": [
          {
            "href": "https://trunk.test/wp-json/wp/v2/sidebars"
          }
        ],
        "self": [
          {
            "href": "https://trunk.test/wp-json/wp/v2/sidebars/sidebar-1"
          }
        ],
        "wp:widget": [
          {
            "embeddable": true,
            "href": "https://trunk.test/wp-json/wp/v2/widgets?sidebar=sidebar-1"
          }
        ],
        "curies": [
          {
            "name": "wp",
            "href": "https://api.w.org/{rel}",
            "templated": true
          }
        ]
      }
    }
    ```

The `widgets` property contains an ordered list of widget ids. While all other properties
are readonly, the `widgets` property can be used to reorder a sidebar’s assigned
widgets. Any widget ids omitted when updating the sidebar will be assigned to the
Inactive Widgets sidebar area.

For example, making a `PUT` request to `/wp/v2/sidebars/sidebar-1` with the following
body will remove the Recent Comments widget, and move the Meta widget to the top
of the sidebar.

    ```wp-block-code
    PUT /wp/v2/sidebars/sidebar-1
    ```

    ```notranslate
    {
      "widgets": [
        "meta-1",
        "recent-posts-2"
      ]
    }
    ```

For more information about the changes to widgets in 5.8, check out the Block-based
Widgets Editor dev notedev note Each important change in WordPress Core is documented
in a developers note, (usually called dev note). Good dev notes generally include
a description of the change, the decision that led to this change, and a description
of how developers are supposed to work with that change. Dev notes are published
on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev
notes is particularly important when plugin/theme authors and WordPress developers
need to be aware of those changes.In general, all dev notes are compiled into a 
Field Guide at the beginning of the release candidate phase..

> [Block-based Widgets Editor in WordPress 5.8](https://make.wordpress.org/core/2021/06/29/block-based-widgets-editor-in-wordpress-5-8/)

## Additional Changes

### Posts Collection Tax Query Accepts `operator`

By default, a post must contain at least one of the requested terms to be included
in the response. As of [[51026]](https://core.trac.wordpress.org/changeset/51026),
the REST API accepts a new `operator` property that can be set to `AND` to require
a post to contain all of the requested terms.

For example, `/wp/v2/posts?tags[terms]=1,2,3&tags[operator]=AND` will return posts
that have tags with the ids of 1, 2, and 3.

See [#41287](https://core.trac.wordpress.org/ticket/41287) for more details.

_Props to [@noisysocks](https://profiles.wordpress.org/noisysocks/) for reviewing._

[#5-8](https://make.wordpress.org/core/tag/5-8/), [#dev-notes](https://make.wordpress.org/core/tag/dev-notes/),
[#rest-api](https://make.wordpress.org/core/tag/rest-api/)

 [  ](https://profiles.wordpress.org/timothyblynjacobs/) [Timothy Jacobs](https://profiles.wordpress.org/timothyblynjacobs/)
7:59 pm _on_ February 23, 2021     
Tags: [5.7 ( 67 )](https://make.wordpress.org/core/tag/5-7/),
[dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/), rest-api   

# 󠀁[REST API Changes in WordPress 5.7](https://make.wordpress.org/core/2021/02/23/rest-api-changes-in-wordpress-5-7/)󠁿

The following is a snapshot of some of the changes to the REST APIREST API The REST
API is an acronym for the RESTful Application Program Interface (API) that uses 
HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application(
think “phone app” or “website”) can communicate with the data store (think “database”
or “file system”) [https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/)
in WordPress 5.7. For more details, see the full list of [closed tickets](https://core.trac.wordpress.org/query?component=REST+API&milestone=5.7&or&focuses=~rest-api&milestone=5.7&or&component=Application+Passwords&milestone=5.7&col=id&col=summary&col=status&col=owner&col=type&col=priority&col=version&order=priority).

## Endpoint Changes

### Posts Collection Tax Query Accepts `include_children`

Introduced in [50157](https://core.trac.wordpress.org/changeset/50157), the REST
API posts collection endpoints have been updated to allow a more complex syntax 
for specifying the `tax_query` used when querying posts. Each taxonomyTaxonomy A
taxonomy is a way to group things together. In WordPress, some common taxonomies
are category, link, tag, or post format. [https://codex.wordpress.org/Taxonomies#Default_Taxonomies](https://codex.wordpress.org/Taxonomies#Default_Taxonomies).’
s query parameters can now both accept a list of term ids or an object with a `terms`
property. 

Hierarchical taxonomies support an `include_children` property alongside `terms`.
By default it’s disabled, but if set to `true` the flag is enabled for the generated
[`tax_query`](https://developer.wordpress.org/reference/classes/wp_query/#taxonomy-parameters)
to enable searching for posts that have the given terms or terms that are children
of the given terms.

    ```notranslate
    import { addQueryArgs } from '@wordpress/url';
    import apiFetch from '@wordpress/api-fetch';

    const query = {
    	categories: {
    		terms: [ 3, 5, 7 ],
    		include_children: true,
    	},
    };
    apiFetch( { path: addQueryArgs( '/wp/v2/posts', query ) } );
    ```

See [#39494](https://core.trac.wordpress.org/ticket/39494) for more details.

### Support `modified_before` and `modified_after` Query Parameters

Introduced in [50024](https://core.trac.wordpress.org/changeset/50024) the REST 
API posts collection endpoints now accept `modified_before` and `modified_after`
query parameters to query posts based on the post modified date instead of the post
published date.

    ```notranslate
    import { addQueryArgs } from '@wordpress/url';
    import apiFetch from '@wordpress/api-fetch';

    const query = {
    	modified_after: '2020-12-01T00:00:00Z',
    	modified_before: '2020-12-31T23:59:59Z',
    };
    apiFetch( { path: addQueryArgs( '/wp/v2/posts', query ) } );
    ```

As a result of this change, the posts controller now uses a `date_query` with a 
separate clause for each date related query parameter instead of a single clause
with the `before` and `after` flags set.

See [#50617](https://core.trac.wordpress.org/ticket/50617) for more details.

### Multiple MetaMeta Meta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress. Values for a Key Can Be Deleted by Sending an Empty Array

Previously, to remove all values for a specific meta key, passing `null` as the 
value for that meta key was required. After [49966](https://core.trac.wordpress.org/changeset/49966)
an empty array can be passed instead.

    ```notranslate
    register_post_meta( 'post', 'my_meta_key', array( 
    	'type' => 'string',
    	'show_in_rest' => true,
    ) );
    ```

    ```notranslate
    import apiFetch from '@wordpress/api-fetch';

    const data = {
    	meta: {
    		my_meta_key: [],
    	}
    };
    apiFetch( { data, method: 'PUT', path: '/wp/v2/posts/1' } );
    ```

See [#50790](https://core.trac.wordpress.org/ticket/50790) for more details.

### All Themes are Returned via the Themes Controller

As of [49925](https://core.trac.wordpress.org/changeset/49925), the themes endpoint
now returns all themes installed on the site, not just the active theme. By default,
the endpoint returns both `active` and `inactive` themes, but the `status` query
parameter can be used to limit the list to themes with the desired status.

Note, the `theme_supports` value is only exposed for the `active` theme. The field
is omitted for `inactive` themes since declaring the supported theme features requires
calls to `add_theme_support` which can only be done if the theme is active.

When querying solely for `active` themes, the only permission required is to be 
able to edit posts of any `show_in_rest` post type. However when querying for `inactive`
themes the `switch_themes` or `manage_network_themes` capabilitycapability A **capability**
is permission to perform one or more types of task. Checking if a user has a capability
is performed by the `current_user_can` function. Each user of a WordPress site might
have some permissions but not others, depending on their role. For example, users
who have the Author role usually have permission to edit their own posts (the “edit_posts”
capability), but not permission to edit other users’ posts (the “edit_others_posts”
capability). is required.

In addition to the changes to the collection endpoint, a new single theme endpoint
is available. For example, `/wp/v2/themes/twentytwentyone` will return information
about Twenty Twenty One. For convenience, a link is also added to the currently 
active theme in the REST API Index if the current user has the requisite permissions.
This is the recommended way for applications to discover the currently active theme.

    ```notranslate
    {
      "name": "WordPress Develop",
      "_links": {
        "help": [
          {
            "href": "http://v2.wp-api.org/"
          }
        ],
        "wp:active-theme": [
          {
            "href": "https://wordpress.test/wp-json/wp/v2/themes/twentytwentyone"
          }
        ],
        "curies": [
          {
            "name": "wp",
            "href": "https://api.w.org/{rel}",
            "templated": true
          }
        ]
      }
    }
    ```

Lastly, the `page`, `per_page`, `search` and `context` query parameters are no longer
exposed in the index as they are not supported by this endpoint.

See [#50152](https://core.trac.wordpress.org/ticket/50152) for more details.

### Image Editor Accepts a List of Modifiers

The `/wp/v2/media/<id>/edit` endpoint [introduced in WordPress 5.5](https://make.wordpress.org/core/2020/07/16/new-and-modified-rest-api-endpoints-in-wordpress-5-5/#image-editing)
came with a limited APIAPI An API or Application Programming Interface is a software
intermediary that allows programs to interact with each other and share data in 
limited, clearly defined ways. that accepted top-level rotation and crop declarations.
In [50124](https://core.trac.wordpress.org/changeset/50124) this API was made more
powerful and flexible by accepting an array of modifications in the new `modifiers`
request parameter.

    ```notranslate
    import apiFetch from '@wordpress/api-fetch';

    const data = {
    	modifiers: [
    		{
    			type: 'crop',
    			args: {
    				left  : 0,
    				top   : 0,
    				width : 80,
    				height: 80
    			}
    		},
    		{
    			type: 'rotate',
    			args: {
    				angle: 90
    			}
    		}
    	]
    };
    apiFetch( { data, method: 'POST', path: '/wp/v2/media/5/edit' } );
    ```

The previous query parameters have been marked as deprecated, but will continue 
to function as normal and do not currently issue deprecation warnings. Clients are
encouraged to switch to the new syntax.

To alleviate server resources, whenever possible, clients should simplify redundant
modifications before sending the request.

See [#52192](https://core.trac.wordpress.org/ticket/52192) for more details.

## Parameter Validation

### Non-String Enums

In [50010](https://core.trac.wordpress.org/changeset/50010) support for type coercion
was added to the `enum` JSONJSON JSON, or JavaScript Object Notation, is a minimal,
readable format for structuring data. It is used primarily to transmit data between
a server and web application, as an alternative to XML. Schema keyword. Previously,
the `enum` keyword was validated by perform a strict equality check. For `string`
types this is generally ok, but it prevented using alternative types like `number`
when rich type support isn’t available.

Now the same level of type coercion/sanitization is applied when validating enum
as all other validation checks. This means that a value of `"1"` will be accepted
for an enum of `[ 0, 1 ]`. Additionally, `object` types now properly ignore key 
order when checking for equality.

See [#51911](https://core.trac.wordpress.org/ticket/51911) for more details.

### Validation Error Codes

As of [50007](https://core.trac.wordpress.org/changeset/50007), the `rest_validate_value_from_schema`
function now returns specific error codes for each validation failure instead of
the generic `rest_invalid_param`. For instance, if more array items are given than
allowed by `maxItems`, the `rest_too_many_items`error code will be returned.

See [#52317](https://core.trac.wordpress.org/ticket/52317) for more details.

### Return Detailed Error Information from Request Validation

Previously when a parameter failed validation only the first error message specified
in the `WP_Error` instance was returned to the user. Since [50150](https://core.trac.wordpress.org/changeset/50150)
the REST API will now return detailed error information as part of the `details`
error data key.

    ```notranslate
    {
      "code": "rest_invalid_param",
      "message": "Invalid parameter(s): enum_a, enum_b",
      "data": {
        "status": 400,
        "params": {
          "enum_a": "enum_a is not one of a, b, c.",
          "enum_b": "enum_b is not one of d, e, f."
        },
        "details": {
          "enum_a": {
            "code": "rest_not_in_enum",
            "message": "enum_a is not one of a, b, c.",
            "data": {
              "enum": ["a", "b", "c"]
            }
          },
          "enum_b": {
            "code": "rest_not_in_enum",
            "message": "enum_b is not one of d, e, f.",
            "data": {
              "enum": ["d", "e", "f"]
            }
          }
        }
      }
    }
    ```

See [#50617](https://core.trac.wordpress.org/ticket/50617) for more details.

## Application Passwords

### Fine Grained Capabilitiescapability A **capability** is permission to perform one or more types of task. Checking if a user has a capability is performed by the `current_user_can` function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability).

When the Application Passwords REST API controllers were introduced, the `edit_user`
meta capability was used for all permission checks. As of [50114](https://core.trac.wordpress.org/changeset/50114),
the REST API now uses specific meta capabilities for each action type.

 * `create_app_password`
 * `list_app_passwords`
 * `read_app_password`
 * `edit_app_password`
 * `delete_app_password`
 * `delete_app_passwords`

By default, these capabilities all map to `edit_user` however they can now be customized
by using the `map_meta_cap` filterFilter Filters are one of the two types of Hooks
[https://codex.wordpress.org/Plugin_API/Hooks](https://codex.wordpress.org/Plugin_API/Hooks).
They provide a way for functions to modify data of other functions. They are the
counterpart to Actions. Unlike Actions, filters are meant to work in an isolated
manner, and should never have side effects such as affecting global variables and
output..

See [#51703](https://core.trac.wordpress.org/ticket/51703) for more details.

### Introspection Endpoint

[50065](https://core.trac.wordpress.org/changeset/50065) introduces a new Application
Passwords endpoint for introspecting the app password being currently used for authentication.
This endpoint is accessible via `/wp/v2/users/me/application-passwords/introspect`
and will return the same information as the other endpoints. This allows for an 
application to disambiguate between multiple installations of their application 
which would all share the same `app_id`.

Clients can use this information to provide UIUI User interface hints about how 
the user is authenticated, for instance by displaying the App Passwords’s label.
Or when their application is uninstalled by the user, they can automatically clean
up after themselves by deleting their App Password.

See [#52275](https://core.trac.wordpress.org/ticket/52275) for more details.

### Unique Application Names

As of [50030](https://core.trac.wordpress.org/changeset/50030) the Application Passwords
API enforces that each App Password has a unique name that cannot consist solely
of whitespace characters. Additionally, invalidinvalid A resolution on the bug tracker(
and generally common in software development, sometimes also _notabug_) that indicates
the ticket is not a bug, is a support request, or is generally invalid. characters
are stripped from the provided application name.

See [#51941](https://core.trac.wordpress.org/ticket/51941) for more details.

_Props to [@flixos90](https://profiles.wordpress.org/flixos90/), [@desrosj](https://profiles.wordpress.org/desrosj/)
for proofreading._

[#5-7](https://make.wordpress.org/core/tag/5-7/), [#dev-notes](https://make.wordpress.org/core/tag/dev-notes/),
[#rest-api](https://make.wordpress.org/core/tag/rest-api/)

 [  ](https://profiles.wordpress.org/timothyblynjacobs/) [Timothy Jacobs](https://profiles.wordpress.org/timothyblynjacobs/)
12:48 pm _on_ December 8, 2020     
Tags: [5.6 ( 85 )](https://make.wordpress.org/core/tag/5-6/),
[dev-notes ( 648 )](https://make.wordpress.org/core/tag/dev-notes/), rest-api   

# 󠀁[REST API Changes in WordPress 5.6](https://make.wordpress.org/core/2020/12/08/rest-api-changes-in-wordpress-5-6/)󠁿

WordPress 5.6 introduces a number of changes to the REST APIREST API The REST API
is an acronym for the RESTful Application Program Interface (API) that uses HTTP
requests to GET, PUT, POST and DELETE data. It is how the front end of an application(
think “phone app” or “website”) can communicate with the data store (think “database”
or “file system”) [https://developer.wordpress.org/rest-api/](https://developer.wordpress.org/rest-api/),
some of which have been covered in other dev notesdev note Each important change
in WordPress Core is documented in a developers note, (usually called dev note).
Good dev notes generally include a description of the change, the decision that 
led to this change, and a description of how developers are supposed to work with
that change. Dev notes are published on Make/Core blog during the beta phase of 
WordPress release cycle. Publishing dev notes is particularly important when plugin/
theme authors and WordPress developers need to be aware of those changes.In general,
all dev notes are compiled into a Field Guide at the beginning of the release candidate
phase..

> [Application Passwords: Integration Guide](https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/)

> [REST API Batch Framework in WordPress 5.6](https://make.wordpress.org/core/2020/11/20/rest-api-batch-framework-in-wordpress-5-6/)

> [Site Health Check changes in 5.6](https://make.wordpress.org/core/2020/11/15/site-health-check-changes-in-5-6/)

> [New action wp_after_insert_post in WordPress 5.6.](https://make.wordpress.org/core/2020/11/20/new-action-wp_after_insert_post-in-wordpress-5-6/)

Below are some other noteworthy changes that deserve a call out.

## Search

The `wp/v2/search` endpoint introduced in WordPress 5.0 provides a unified interface
for searching across multiple content types. WordPress 5.6 adds a terms and post
formats search handler. For example, to search across terms in all taxonomies, make
the following request: `https://example.org/wp-json/wp/v2/search?type=term&search
=my-term`. To search post-formats, use the `post-format` type: `https://example.
org/wp-json/wp/v2/search?type=post-format&search=aside`.

Additionally, the REST API search infrastructure no longer requires that the `id`
for each item is an integer. Strings are now an acceptable `id` type.

See [#51458](https://core.trac.wordpress.org/ticket/51458), [#51459](https://core.trac.wordpress.org/ticket/51459),
[#51131](https://core.trac.wordpress.org/ticket/51131).

## JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. Schema

### `multipleOf` keyword

The `multipleOf` keyword allows for asserting that an `integer` or `number` type
is a multiple of the given number. For example, this schema will only accept even
integers.

    ```notranslate
    {
      "type": "integer",
      "multipleOf": 2
    }
    ```

`multipleOf` also supports decimals. For example, this schema could be used to accept
a percentage with a maximum of 1 decimal point.

    ```notranslate
    {
      "type": "number",
      "minimum": 0,
      "maximum": 100,
      "multipleOf": 0.1
    }
    ```

See [#51022](https://core.trac.wordpress.org/ticket/51022).

### `minProperties` and `maxProperties`

The `minItems` and `maxItems` keywords can be used for the `array` type. The `minProperties`
and `maxProperties` introduces this same functionality for the `object` type. This
is helpful when using `additionalProperties` to have a list of objects with unique
keys.

This schema requires an object that specifies at least 1, and at most 3, colors.

    ```notranslate
    {
      "type": "object",
      "additionalProperties": {
        "type": "string",
        "format": "hex-color"
      },
      "minProperties": 1,
      "maxProperties": 3
    }
    ```

    ```notranslate
    {
      "primary": "#52accc",
      "secondary": "#096484"
    }
    ```

See [#51023](https://core.trac.wordpress.org/ticket/51023).

### `patternProperties`

The `patternProperties` keyword is similar to the `additionalProperties` keyword,
but allows for asserting that the property matches a regex pattern. The keyword 
is an object where each property is a regex [pattern](https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#pattern)
and its value is the JSON Schema used to validate properties that match that pattern.

For example, this schema requires that each value is a hex color and the property
must only contain “word” characters.

    ```notranslate
    {
      "type": "object",
      "patternProperties": {
        "^\\w+$": {
          "type": "string",
          "format": "hex-color"
        }
      },
      "additionalProperties": false
    }
    ```

When the REST API validates the `patternProperties` schema, if a property doesn’t
match any of the patterns, the property will be allowed and not have any validation
applied to its contents. This behaves the same as the `properties` keyword. If this
logic isn’t desired, add `additionalProperties` to the schema to disallow non-matching
properties. See [#51024](https://core.trac.wordpress.org/ticket/51024).

### `oneOf` and `anyOf`

These are advanced keywords that allow for the JSON Schema validator to choose one
of _many_ schemas to use when validating a value. The `anyOf` keyword allows for
a value to match at least one of the given schemas. Whereas, the `oneOf` keyword
requires the value match _exactly_ one schema.

For example, this schema allows for submitting an array of “operations” to an endpoint.
Each operation can either be a “crop” or a “rotation”.

    ```notranslate
    {
      "type": "array",
      "items": {
        "oneOf": [
          {
            "title": "Crop",
            "type": "object",
            "properties": {
              "operation": {
                "type": "string",
                "enum": [
                  "crop"
                ]
              },
              "x": {
                "type": "integer"
              },
              "y": {
                "type": "integer"
              }
            }
          },
          {
            "title": "Rotation",
            "type": "object",
            "properties": {
              "operation": {
                "type": "string",
                "enum": [
                  "rotate"
                ]
              },
              "degrees": {
                "type": "integer",
                "minimum": 0,
                "maximum": 360
              }
            }
          }
        ]
      }
    }
    ```

The REST API will loopLoop The Loop is PHP code used by WordPress to display posts.
Using The Loop, WordPress processes each post to be displayed on the current page,
and formats it according to how it matches specified criteria within The Loop tags.
Any HTML or PHP code in the Loop will be processed on each post. [https://codex.wordpress.org/The_Loop](https://codex.wordpress.org/The_Loop)
over each schema specified in the `oneOf` array and look for a match. If exactly
one schema matches, then validation will succeed. If more than one schema matches,
validation will fail. If no schemas match, then the validator will try to find the
closest matching schema and return an appropriate error message.

> operations[0] is not a valid Rotation. Reason: operations[0][degrees] must be 
> between 0 (inclusive) and 360 (inclusive)

See [#51025](https://core.trac.wordpress.org/ticket/51025).

### Expose all JSON Schema keywords in the index

When making an `OPTIONS` request to an endpoint, the REST API will return the `args`
that the route accepts. Previously, only a limited subset of JSON Schema keywords
were exposed. In WordPress 5.6, now the full list of JSON Schema keywords that the
REST API supports will be exposed. See [#51020](https://core.trac.wordpress.org/ticket/51020).

#### More specific validation error codes

When the `type` of a value is incorrect, `rest_validate_value_from_schema` now returns`
rest_invalid_type` instead of the generic `rest_invalid_param`. The validation error
code is not currently exposed to REST API clients. This would only effect direct
usages of the validation function.

## Miscellaneous

The `apiRequest` library now supports using the `PUT` and `DELETE` HTTPHTTP HTTP
is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol 
used by the World Wide Web and this protocol defines how messages are formatted 
and transmitted, and what actions Web servers and browsers should take in response
to various commands. methods with servers that didn’t support those methods. This
is done by making the request a `POST` request and passing the original HTTP method
in the `X-HTTP-Method-Override` headerHeader The header of your site is typically
the first thing people will experience. The masthead or header art located across
the top of your page is part of the look and feel of your website. It can influence
a visitor’s opinion about your content and you/ your organization’s brand. It may
also look different on different screen sizes.. [#43605](https://core.trac.wordpress.org/ticket/43605).

The comments controller now uses the `rest_get_route_for_post` function introduced
in WordPress 5.5 to generate the `up` response link. This function is filterable
to allow for [custom controllers to properly define](https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-rest-api-support-for-custom-content-types/#registering-a-custom-post-type-with-rest-api-support)
their REST API route. [#44152](https://core.trac.wordpress.org/ticket/44152).

The REST API now supports a broader range of JSON media types. Previously, only `
application/json` was supported which prevented using subtypes like `application/
activity+json`. The REST API will now `json_decode` the body of requests using a
JSON subtype `Content-Type`. Additionally, `wp_die()` now properly sends the error
as JSON when a JSON subtype is specified in the `Accept` header. [#49404](https://core.trac.wordpress.org/ticket/49404).

_Props [@m\_butcher](https://profiles.wordpress.org/m_butcher/) for reviewing._

[#5-6](https://make.wordpress.org/core/tag/5-6/), [#dev-notes](https://make.wordpress.org/core/tag/dev-notes/),
[#rest-api](https://make.wordpress.org/core/tag/rest-api/)

# Post navigation

[← Older posts](https://make.wordpress.org/core/tag/rest-api/page/2/?output_format=md)