AI Summary
WPForms 1.9.9 introduced a REST API built on top of the WordPress Abilities API, and WPForms 1.10.2 expanded it with opt-in write support. You can list forms, fetch form configuration, retrieve and search entries, and pull form statistics, and, once write access is enabled, create forms, add and update fields, and update safe form settings. Every ability works 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/<ability>/run and to MCP-compatible AI clients through the official MCP adapter plugin.
WPForms registers a set of abilities under the wpforms/ namespace. The read abilities have been available since WPForms 1.9.9. WPForms 1.10.2 added a set of write abilities that are disabled by default (see Enabling Write Access below). 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 for the read abilities, or 1.10.2 or later for the write abilities (some abilities are Pro only; see the reference table below)
- For MCP clients: the 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 request to /wp-json/wp-abilities/v1/abilities/<ability>/run. The HTTP method depends on the ability: read abilities use GET, and write abilities use POST. Calling an ability with the wrong method returns 405 Method Not Allowed.
Note: Read abilities (GET) take their 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 ]). Write abilities (POST) take the same parameters as a JSON object under an input key in the request body. The PHP transport handles both the same way: pass an associative array to execute().
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
- Sign in as the WordPress user whose permissions the calls should run under.
- Go to Users » Profile and scroll to the Application Passwords section.
- Enter a name for the integration and click Add New Application Password.
- 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.
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 <encoded>.
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.
Enabling Write Access
The read abilities are always available. The write abilities (wpforms/create-form, wpforms/add-field, wpforms/update-field, and wpforms/update-form-settings) are disabled by default and must be turned on before they will run. There are two ways to enable them, and the filter always has the final say.
Enabling Writes in Code
Enable writes programmatically with the wpforms_integrations_abilities_allow_write filter:
add_filter( 'wpforms_integrations_abilities_allow_write', '__return_true' );
The filter overrides the no-code toggle in both directions. Returning true enables writes even when the toggle is off, and returning false disables them even when the toggle is on. This keeps existing developer setups working unchanged.
Enabling Writes Without Code
Site owners can enable writes from WPForms » Tools » AI MCP with the Enable MCP Write Access toggle, which persists to the ai-mcp-write-enabled key in the wpforms_settings option. The filter above takes precedence over this toggle. For the full walkthrough, see Using WPForms With AI Assistants.
Note: While writes are disabled, the write abilities are hidden from MCP discovery and any attempt to run one returns 403 with the code wpforms_writes_disabled. Input validation runs before the write gate, so malformed input returns a 400 even when writes are off.
Permissions
Each ability checks a specific WPForms capability before executing. Failed checks return a WP_Error with HTTP status 403.
| Ability | Capability |
|---|---|
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 |
The write abilities added in WPForms 1.10.2 enforce the same capability model. wpforms/create-form requires the capability to create forms, and wpforms/add-field, wpforms/update-field, and wpforms/update-form-settings require the capability to edit the target form. A failed check returns a WP_Error with HTTP status 403. Write abilities also require write access to be enabled, as described above.
Ability Reference
The read abilities are idempotent and safe to call repeatedly. The write abilities, added in WPForms 1.10.2, create or modify form data and require write access to be enabled first. Errors are returned as WP_Error objects serialized to JSON with code, message, and data.status fields.
wpforms/list-forms
List forms with summary metadata. Available in Lite and Pro.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
status | string | No | publish | Form status. One of publish, draft, trash. |
limit | integer | No | 20 | Maximum number of forms to return. Range 1 to 100. |
offset | integer | No | 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
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
form_id | integer | Yes | None | The ID of the form to retrieve. |
include_fields | boolean | No | 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
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
form_id | integer | Yes | None | 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
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
form_id | integer | Yes | None | The ID of the form to fetch entries for. |
status | string | No | "" | One of partial, abandoned, spam, trash. Empty returns all completed entries. |
type | string | No | "" | One of read, unread, starred. |
include_fields | boolean | No | false | Include each entry’s field values in the response. |
limit | integer | No | 20 | Maximum number of entries to return. Range 1 to 100. |
offset | integer | No | 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
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
entry_id | integer | Yes | None | The ID of the entry to retrieve. |
include_fields | boolean | No | 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
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
form_id | integer | No | None | Restrict the search to a single form. Omit to search across all forms. |
search | string | No | "" | Full-text query matched against all entry fields. |
field_id | integer | No | None | Restrict the search to a specific field ID. Use with field_value. |
field_value | string | No | None | Exact value to match in the field specified by field_id. |
date_from | string | No | None | Start of date range, format YYYY-MM-DD. |
date_to | string | No | None | End of date range, format YYYY-MM-DD. |
status | string | No | "" | One of partial, abandoned, spam, trash. |
type | string | No | "" | One of read, unread, starred. |
include_fields | boolean | No | true | Include entry field values in the response. |
limit | integer | No | 20 | Maximum number of entries per page. Range 1 to 100. |
page | integer | No | 1 | Page number. Note that this ability uses page-based pagination, unlike list-forms and get-entry-summaries, which use offset. |
orderby | string | No | date | One of entry_id, date, status. |
order | string | No | 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": "[email protected]", "type": "email" }
]
}
],
"total": 47,
"total_pages": 5,
"page": 1,
"limit": 20
}
The abilities below were added in WPForms 1.10.2. All of them require write access to be enabled (see Enabling Write Access), except wpforms/describe-editing-schema, which is a read ability used to discover what the write abilities accept. The write abilities run as POST requests over REST, or through execute() in PHP.
wpforms/describe-editing-schema
Return the field types and form settings that the write abilities accept on this site. Call this first, because the available field types depend on the license level. Available in Lite and Pro.
Note: Over REST this ability is a GET and requires a non-empty input object. Calling it with no parameters returns 400 with the message input is not of type object. Include at least an empty bracketed key, for example input%5B%5D=. In PHP, pass an empty array to execute().
Example Request
curl "$WP_SITE/wp-json/wp-abilities/v1/abilities/wpforms/describe-editing-schema/run?input%5B%5D="
Example Response (truncated)
{
"field_types": [
{
"type": "text",
"label": "Single Line Text",
"properties": [
{ "key": "label", "schema": { "type": "string" } },
{ "key": "required", "schema": { "type": "boolean" } },
{ "key": "size", "schema": { "type": "string", "enum": ["small", "medium", "large"] } },
{ "key": "placeholder", "schema": { "type": "string" } },
{ "key": "default_value", "schema": { "type": "string" } }
],
"required_props": []
},
{
"type": "select",
"label": "Dropdown",
"properties": [
{ "key": "choices", "schema": { "type": "array", "items": { "type": "object", "properties": { "label": { "type": "string" }, "value": { "type": "string" } }, "required": ["label"] } } },
{ "key": "style", "schema": { "type": "string", "enum": ["classic", "modern"] } }
],
"required_props": []
}
],
"form_settings": [
{ "key": "form_title", "schema": { "type": "string" }, "description": "The form title." },
{ "key": "form_desc", "schema": { "type": "string" }, "description": "The form description." },
{ "key": "submit_text", "schema": { "type": "string" }, "description": "The submit button label." }
]
}
On Pro, field_types lists text, textarea, email, number, select, radio, checkbox, name, phone, date-time, and file-upload. On Lite, the set is the base eight: text, textarea, email, number, select, radio, checkbox, and name. The slugs match the builder, so the Dropdown field is select and the Date / Time field is date-time.
Field Properties
The create-form, add-field, and update-field abilities accept these field properties. Each property applies only to the field types that declare it, as reported by describe-editing-schema.
| Property | Type | Applies To | Description |
|---|---|---|---|
label | string | All types | The field label. |
description | string | All types | The field description. |
required | boolean | All types | Whether the field is required. |
size | string | All types | Field size. One of small, medium, large. |
label_hide | boolean | All types | Hide the field label on the frontend. |
placeholder | string | text, textarea, email, number, phone | Placeholder text. |
default_value | string | text, textarea, email, number, phone | Default value. |
choices | array | select, radio, checkbox | Ordered list of { "label": "...", "value": "..." } objects. value is optional and derives from the label when omitted. |
style | string | select, file-upload | Display style. One of classic, modern. |
input_columns | string | radio, checkbox | Column layout. One of "", inline, 2, 3. |
extensions | string | file-upload | Comma-separated list of allowed file extensions. |
max_size | integer | file-upload | Maximum file size. Minimum 1. |
max_file_number | integer | file-upload | Maximum number of files. Minimum 1. |
media_library | boolean | file-upload | Store uploads in the WordPress Media Library. |
wpforms/create-form
Create a new form with an optional set of fields and settings, in a single call. Available in Lite and Pro.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | Yes | None | The form title. |
fields | array | No | None | Initial fields. Each item needs a type plus any field properties for that type. |
settings | object | No | None | Initial settings. One or more of form_title, form_desc, submit_text. |
Note: The input schema sets additionalProperties to false, so only title, fields, and settings are accepted at the top level. A top-level description key is rejected with 400; set the form description through settings.form_desc instead.
Example Request
$ability = wp_get_ability( 'wpforms/create-form' );
$result = $ability->execute(
[
'title' => 'Job Application',
'fields' => [
[ 'type' => 'name', 'label' => 'Your Name', 'required' => true ],
[ 'type' => 'email', 'label' => 'Email', 'required' => true ],
[ 'type' => 'select', 'label' => 'Position', 'choices' => [ [ 'label' => 'Baker' ], [ 'label' => 'Cashier' ] ] ],
],
'settings' => [
'form_title' => 'Job Application',
'form_desc' => 'Apply to join our team.',
'submit_text' => 'Apply Now',
],
]
);
Example Response
{
"form_id": 1102,
"fields": [
{ "id": 0, "type": "name" },
{ "id": 1, "type": "email" },
{ "id": 2, "type": "select" }
]
}
wpforms/add-field
Add a new field to an existing form. Available in Lite and Pro.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
form_id | integer | Yes | None | The ID of the form to add the field to. |
type | string | Yes | None | The field type slug. Call describe-editing-schema for the available types. |
Pass any field properties for the chosen type alongside form_id and type (see the Field Properties table above).
Example Request
$ability = wp_get_ability( 'wpforms/add-field' );
$result = $ability->execute(
[
'form_id' => 1102,
'type' => 'phone',
'label' => 'Phone',
'required' => true,
]
);
Example Response
{
"form_id": 1102,
"field_id": 6,
"type": "phone"
}
Note: Requesting a field type that is not available on the site, such as a Pro type on Lite or an unsupported type, returns 422 with the code wpforms_field_type_unavailable.
wpforms/update-field
Update properties of an existing field. Available in Lite and Pro.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
form_id | integer | Yes | None | The ID of the form that contains the field. |
field_id | integer | Yes | None | The ID of the field to update. |
Pass the field properties you want to change alongside form_id and field_id (see the Field Properties table above). Properties you omit are left unchanged. When you send choices, the new list is merged by index onto the existing choices.
Example Request
$ability = wp_get_ability( 'wpforms/update-field' );
$result = $ability->execute(
[
'form_id' => 1102,
'field_id' => 6,
'required' => true,
'placeholder' => 'e.g. 555-123-4567',
]
);
Example Response
{
"form_id": 1102,
"field_id": 6,
"updated": ["required", "placeholder"],
"ignored": []
}
wpforms/update-form-settings
Update a form’s safe settings. The v1 whitelist is form_title, form_desc, and submit_text. Available in Lite and Pro.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
form_id | integer | Yes | None | The ID of the form to update. |
settings | object | Yes | None | One or more of form_title, form_desc, submit_text. |
Example Request
$ability = wp_get_ability( 'wpforms/update-form-settings' );
$result = $ability->execute(
[
'form_id' => 1102,
'settings' => [
'submit_text' => 'Send Application',
],
]
);
Example Response
{
"form_id": 1102,
"updated": ["submit_text"],
"ignored": []
}
Keys that are not in the whitelist are returned in the ignored array rather than causing an error. For example, sending ajax_submit leaves it unchanged and lists it under ignored.
Using WPForms With MCP Clients
The read abilities are registered with mcp.public set to true, so MCP-compatible AI clients (Claude Desktop, Cursor, and others) discover them automatically once the WordPress site is connected through the wordpress/mcp-adapter plugin. The write abilities and wpforms/describe-editing-schema are exposed to MCP only when write access is enabled; while writes are off, their mcp.public flag is false and they are hidden from MCP discovery, so an assistant may report that it cannot find the WPForms write tools. Once a site is connected and write access is on, 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.
The write abilities appear in this index on any site running WPForms 1.10.2 or later, whether or not write access is enabled. Their mcp.public flag reflects the write-access state, however: it is true only when writes are enabled, which is why MCP clients see the write tools only after write access is turned on.
Extending the Field Registry
The field types and properties that the write abilities accept are defined in an extensible registry. Addons can register their own field types and properties through two filters, so a custom or addon field type can become available to create-form, add-field, and update-field.
wpforms_integrations_abilities_field_registry_typesregisters new field types. Each type declares its availability through arequiresvalue, which is the stringpro, an addon slug, or a callable, and can specify its required properties.wpforms_integrations_abilities_field_registry_propertiescontributes new property definitions that field types can reference.
add_filter(
'wpforms_integrations_abilities_field_registry_types',
function ( $types ) {
// Register one or more field types here, then return the list.
return $types;
}
);
Related Links
- Using WPForms With AI Assistants, the no-code guide for connecting an AI assistant
- Introducing the WordPress Abilities API (developer.wordpress.org)
- wordpress/mcp-adapter on GitHub
- Model Context Protocol