### [WPForms REST API and Abilities API Reference](https://wpforms.com/developers/wpforms-rest-api/)

**Published:** May 12, 2026
**Author:** Umair Majeed

**Content:**

WPForms 1.9.9 introduced a read-only REST API built on top of the WordPress Abilities API. You can list forms, fetch form configuration, retrieve and search entries, and pull form statistics from any HTTP client, the command line, your own PHP code, or AI assistants that speak the Model Context Protocol (MCP).

If you searched for “WPForms REST API” and landed here, this is it. There is no separate REST API; the Abilities API integration *is* how WPForms exposes its data over HTTP.

## What Is the Abilities API

The Abilities API is a WordPress core feature added in WordPress 6.9. It lets plugins declare individual capabilities (called *abilities*) with a name, an input schema, an output schema, and a permission callback. WordPress then exposes every registered ability automatically over the REST API at `/wp-json/wp-abilities/v1/abilities//run` and to MCP-compatible AI clients through the official MCP adapter plugin.

WPForms registers a set of read-only abilities under the `wpforms/` namespace. Each ability runs the same WPForms capability checks used in the admin (`wpforms_current_user_can()`), so the REST and MCP surfaces inherit the existing permission model rather than introducing a new one.

**Requirements**:

- WordPress 6.9 or later
- WPForms Lite or Pro 1.9.9 or later (some abilities are Pro only; see the reference table below)
- For MCP clients: the [wordpress/mcp-adapter](https://github.com/WordPress/mcp-adapter) plugin

## Calling an Ability

Every ability can be invoked through two transports. The result is identical; pick whichever fits the calling environment.

### REST API

Send an authenticated `GET` request to `/wp-json/wp-abilities/v1/abilities//run`. Because every WPForms ability is read-only, only `GET` is accepted; `POST` returns `405 Method Not Allowed`.

**Note:** Pass parameters as bracketed query string fields under the `input` key, for example `input[limit]=10&input[status]=publish`. URL-encode the brackets if your client does not do it automatically (`%5B` for `[`, `%5D` for `]`).

```

curl "$WP_SITE/wp-json/wp-abilities/v1/abilities/wpforms/list-forms/run?input%5Blimit%5D=10&input%5Bstatus%5D=publish"
```

### PHP

From any plugin, theme, or custom code that runs after the `wp_abilities_api_init` action has fired, fetch the ability with `wp_get_ability()` and call its `execute()` method. Pass the input as an associative array of parameters (the same names listed in each ability’s parameters table).

```

$ability = wp_get_ability( 'wpforms/list-forms' );

if ( $ability ) {
    $result = $ability->execute(
        [
            'limit'  => 10,
            'status' => 'publish',
        ]
    );

    if ( is_wp_error( $result ) ) {
        // Handle error.
        return;
    }

    // $result['forms']  array of form summaries
    // $result['total']  total count (integer)
}
```

## Authentication

The REST transport uses standard WordPress authentication. The recommended method for external clients is **Application Passwords**, which are built into WordPress core and do not require any extra plugin.

### Generating an Application Password

1. Sign in as the WordPress user whose permissions the calls should run under.
2. Go to **Users » Profile** and scroll to the **Application Passwords** section.
3. Enter a name for the integration and click **Add New Application Password**.
4. Copy the generated password. WordPress only shows it once.

For a deeper reference, including REST endpoints for managing application passwords programmatically, see the WordPress core team’s [Application Passwords Integration Guide](https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/).

### Sending the Credentials

Pass the username and application password as HTTP Basic authentication on every request.

#### With curl

Set your site URL as an environment variable so the examples in this doc are runnable as-is:

```

export WP_SITE="https://your-wordpress-site.com"
```

Then add the `-u` flag to every curl command. The flag’s value is your username, followed by a colon, followed by your application password (no spaces).

#### With Postman, Insomnia, or other clients

Set the request’s **Auth** type to **Basic Auth** and provide your username and application password. The client handles the encoding for you.

#### Setting the header manually

Send your credentials as a base64-encoded `Authorization` header:

Combine your username and application password into a single string, separated by a colon (no spaces), and base64-encode the result using a tool of your choice (`base64` command, an online encoder, or your language’s standard library). Send the encoded value on every request as the `Authorization` header, in the form `Authorization: Basic `.

The request examples in the rest of this doc omit the authentication flag for readability. Add the `-u` flag (or the `Authorization` header) to every real request, or the API returns `401 Unauthorized`.

## Permissions

Each ability checks a specific WPForms capability before executing. Failed checks return a `WP_Error` with HTTP status `403`.

AbilityCapability`wpforms/list-forms``view_forms``wpforms/get-form``view_form_single``wpforms/get-form-stats` (Lite)`view_form_single``wpforms/get-form-stats` (Pro)`view_entries_form_single``wpforms/get-entry-summaries``view_entries_form_single``wpforms/get-entry``view_entry_single``wpforms/search-entries` (with `form_id`)`view_entries_form_single``wpforms/search-entries` (without `form_id`)`view_entries`## Ability Reference

All abilities are read-only and idempotent. Errors are returned as `WP_Error` objects serialized to JSON with `code`, `message`, and `data.status` fields.

**Note:** The example requests below omit the authentication flag for readability. Each call must be authenticated, or it returns `401 Unauthorized`. Add the `-u` flag (or send the equivalent `Authorization: Basic` header) to every curl command. See the Authentication section for details.

### wpforms/list-forms

List forms with summary metadata. Available in Lite and Pro.

#### Parameters

NameTypeRequiredDefaultDescription`status`stringNo`publish`Form status. One of `publish`, `draft`, `trash`.`limit`integerNo`20`Maximum number of forms to return. Range 1 to 100.`offset`integerNo`0`Number of forms to skip.#### Example Request

```

curl "$WP_SITE/wp-json/wp-abilities/v1/abilities/wpforms/list-forms/run?input%5Blimit%5D=10&input%5Bstatus%5D=publish"
```

#### Example Response

```

{
  "forms": [
    {
      "id": 123,
      "title": "Contact Form",
      "status": "publish",
      "created": "2026-01-27 10:00:00",
      "modified": "2026-02-15 14:30:00",
      "author": 1
    }
  ],
  "total": 5
}
```

### wpforms/get-form

Retrieve a single form, including a curated subset of its settings and, optionally, its field configuration. Available in Lite and Pro.

#### Parameters

NameTypeRequiredDefaultDescription`form_id`integerYes—The ID of the form to retrieve.`include_fields`booleanNo`true`Include the form’s field configuration in the response.#### Example Request

```

curl "$WP_SITE/wp-json/wp-abilities/v1/abilities/wpforms/get-form/run?input%5Bform_id%5D=123&input%5Binclude_fields%5D=true"
```

#### Example Response

```

{
  "id": 123,
  "title": "Contact Form",
  "status": "publish",
  "created": "2026-01-27 10:00:00",
  "modified": "2026-02-15 14:30:00",
  "author": 1,
  "settings": {
    "form_title": "Contact Form",
    "form_desc": "Get in touch with us",
    "submit_text": "Submit",
    "ajax_submit": true,
    "honeypot": true,
    "antispam": true
  },
  "fields": [
    {
      "id": 1,
      "type": "text",
      "label": "Name",
      "description": "",
      "required": true,
      "size": "medium"
    }
  ]
}
```

The `settings` object returned by this ability is a curated, non-sensitive subset (title, description, submit text, AJAX submit flag, honeypot, anti-spam). Notification, confirmation, and integration settings are not exposed.

### wpforms/get-form-stats

Return submission statistics for a form. The response shape differs between Lite and Pro.

#### Parameters

NameTypeRequiredDefaultDescription`form_id`integerYes—The ID of the form.#### Example Request

```

curl "$WP_SITE/wp-json/wp-abilities/v1/abilities/wpforms/get-form-stats/run?input%5Bform_id%5D=123"
```

#### Lite Response

```

{
  "form_id": 123,
  "entries_available": false,
  "message": "Entry statistics require WPForms Pro. Upgrade to access detailed form submission data."
}
```

#### Pro Response

```

{
  "form_id": 123,
  "total_entries": 156,
  "unread_entries": 12,
  "starred_entries": 8,
  "entries_available": true
}
```

### wpforms/get-entry-summaries

Paginated list of entry summaries for a single form. Pro only.

#### Parameters

NameTypeRequiredDefaultDescription`form_id`integerYes—The ID of the form to fetch entries for.`status`stringNo`""`One of `partial`, `abandoned`, `spam`, `trash`. Empty returns all completed entries.`type`stringNo`""`One of `read`, `unread`, `starred`.`include_fields`booleanNo`false`Include each entry’s field values in the response.`limit`integerNo`20`Maximum number of entries to return. Range 1 to 100.`offset`integerNo`0`Number of entries to skip.#### Example Request

```

curl "$WP_SITE/wp-json/wp-abilities/v1/abilities/wpforms/get-entry-summaries/run?input%5Bform_id%5D=123&input%5Btype%5D=unread&input%5Blimit%5D=20"
```

#### Example Response

```

{
  "entries": [
    {
      "id": 456,
      "form_id": 123,
      "date": "2026-02-15 14:32:10",
      "status": "",
      "viewed": false,
      "starred": true
    }
  ],
  "total": 15,
  "form_id": 123
}
```

### wpforms/get-entry

Retrieve a single entry by ID, including all field values. Pro only.

#### Parameters

NameTypeRequiredDefaultDescription`entry_id`integerYes—The ID of the entry to retrieve.`include_fields`booleanNo`true`Include the entry’s field values in the response.#### Example Request

```

curl "$WP_SITE/wp-json/wp-abilities/v1/abilities/wpforms/get-entry/run?input%5Bentry_id%5D=456"
```

#### Example Response

```

{
  "id": 456,
  "form_id": 123,
  "date": "2026-02-15 14:32:10",
  "modified": "2026-02-15 15:00:00",
  "status": "",
  "viewed": true,
  "starred": false,
  "ip_address": "192.168.1.100",
  "fields": [
    {
      "id": 1,
      "name": "Name",
      "value": "John Doe",
      "type": "text"
    }
  ]
}
```

> The IP address in the response can be masked. Add `add_filter( 'wpforms_abilities_mask_ip_address', '__return_true' )` to your site’s code; when enabled, IPv4 addresses appear with their last three octets replaced by asterisks (for example, `***.***.***.100`).

### wpforms/search-entries

Search entries across one form or all forms with full-text, field-specific, status, and date range filters. Pro only.

#### Parameters

NameTypeRequiredDefaultDescription`form_id`integerNo—Restrict the search to a single form. Omit to search across all forms.`search`stringNo`""`Full-text query matched against all entry fields.`field_id`integerNo—Restrict the search to a specific field ID. Use with `field_value`.`field_value`stringNo—Exact value to match in the field specified by `field_id`.`date_from`stringNo—Start of date range, format `YYYY-MM-DD`.`date_to`stringNo—End of date range, format `YYYY-MM-DD`.`status`stringNo`""`One of `partial`, `abandoned`, `spam`, `trash`.`type`stringNo`""`One of `read`, `unread`, `starred`.`include_fields`booleanNo`true`Include entry field values in the response.`limit`integerNo`20`Maximum number of entries per page. Range 1 to 100.`page`integerNo`1`Page number. Note that this ability uses page-based pagination, unlike `list-forms` and `get-entry-summaries`, which use `offset`.`orderby`stringNo`date`One of `entry_id`, `date`, `status`.`order`stringNo`DESC`One of `ASC`, `DESC`.#### Example Request

```

curl "$WP_SITE/wp-json/wp-abilities/v1/abilities/wpforms/search-entries/run?input%5Bform_id%5D=5&input%5Bsearch%5D=john%40example.com&input%5Bpage%5D=1&input%5Blimit%5D=20"
```

#### Example Response

```

{
  "entries": [
    {
      "id": 142,
      "form_id": 5,
      "date": "2026-02-15 14:32:10",
      "status": "",
      "viewed": false,
      "starred": true,
      "fields": [
        { "id": 1, "name": "Name",  "value": "John Doe",         "type": "text"  },
        { "id": 2, "name": "Email", "value": "john@example.com", "type": "email" }
      ]
    }
  ],
  "total": 47,
  "total_pages": 5,
  "page": 1,
  "limit": 20
}
```

## Using WPForms With MCP Clients

Every WPForms ability is registered with `mcp.public` set to `true`, which means MCP-compatible AI clients (Claude Desktop, Cursor, and others) discover them automatically once the WordPress site is connected through the [wordpress/mcp-adapter](https://github.com/WordPress/mcp-adapter) plugin. After install, no further WPForms-side configuration is required; abilities appear in the client’s tool list under the **WPForms** category and respect the same permission callbacks used by the REST API.

## Discovering Abilities Programmatically

To enumerate the abilities available on a site instead of hard-coding their IDs:

```

# List all registered abilities on the site
curl "$WP_SITE/wp-json/wp-abilities/v1/abilities"

# Fetch the schema for a single ability
curl "$WP_SITE/wp-json/wp-abilities/v1/abilities/wpforms/list-forms"
```

The response includes each ability’s ID, label, description, category, and full input and output schemas, which is enough information to build a generic client without prior knowledge of the WPForms-specific surface.

## Related Links

- [Introducing the WordPress Abilities API](https://developer.wordpress.org/news/2025/11/introducing-the-wordpress-abilities-api/) (developer.wordpress.org)
- [wordpress/mcp-adapter](https://github.com/WordPress/mcp-adapter) on GitHub
- [Model Context Protocol](https://modelcontextprotocol.io/)

**Categories:** Tutorials, Extending

---

