WPForms REST API and Abilities API Reference

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

  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.

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.

AbilityCapability
wpforms/list-formsview_forms
wpforms/get-formview_form_single
wpforms/get-form-stats (Lite)view_form_single
wpforms/get-form-stats (Pro)view_entries_form_single
wpforms/get-entry-summariesview_entries_form_single
wpforms/get-entryview_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.

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
statusstringNopublishForm status. One of publish, draft, trash.
limitintegerNo20Maximum number of forms to return. Range 1 to 100.
offsetintegerNo0Number 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_idintegerYesNoneThe ID of the form to retrieve.
include_fieldsbooleanNotrueInclude 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_idintegerYesNoneThe 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_idintegerYesNoneThe ID of the form to fetch entries for.
statusstringNo""One of partial, abandoned, spam, trash. Empty returns all completed entries.
typestringNo""One of read, unread, starred.
include_fieldsbooleanNofalseInclude each entry’s field values in the response.
limitintegerNo20Maximum number of entries to return. Range 1 to 100.
offsetintegerNo0Number 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_idintegerYesNoneThe ID of the entry to retrieve.
include_fieldsbooleanNotrueInclude 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_idintegerNoNoneRestrict the search to a single form. Omit to search across all forms.
searchstringNo""Full-text query matched against all entry fields.
field_idintegerNoNoneRestrict the search to a specific field ID. Use with field_value.
field_valuestringNoNoneExact value to match in the field specified by field_id.
date_fromstringNoNoneStart of date range, format YYYY-MM-DD.
date_tostringNoNoneEnd of date range, format YYYY-MM-DD.
statusstringNo""One of partial, abandoned, spam, trash.
typestringNo""One of read, unread, starred.
include_fieldsbooleanNotrueInclude entry field values in the response.
limitintegerNo20Maximum number of entries per page. Range 1 to 100.
pageintegerNo1Page number. Note that this ability uses page-based pagination, unlike list-forms and get-entry-summaries, which use offset.
orderbystringNodateOne of entry_id, date, status.
orderstringNoDESCOne 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.

PropertyTypeApplies ToDescription
labelstringAll typesThe field label.
descriptionstringAll typesThe field description.
requiredbooleanAll typesWhether the field is required.
sizestringAll typesField size. One of small, medium, large.
label_hidebooleanAll typesHide the field label on the frontend.
placeholderstringtext, textarea, email, number, phonePlaceholder text.
default_valuestringtext, textarea, email, number, phoneDefault value.
choicesarrayselect, radio, checkboxOrdered list of { "label": "...", "value": "..." } objects. value is optional and derives from the label when omitted.
stylestringselect, file-uploadDisplay style. One of classic, modern.
input_columnsstringradio, checkboxColumn layout. One of "", inline, 2, 3.
extensionsstringfile-uploadComma-separated list of allowed file extensions.
max_sizeintegerfile-uploadMaximum file size. Minimum 1.
max_file_numberintegerfile-uploadMaximum number of files. Minimum 1.
media_librarybooleanfile-uploadStore 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

NameTypeRequiredDefaultDescription
titlestringYesNoneThe form title.
fieldsarrayNoNoneInitial fields. Each item needs a type plus any field properties for that type.
settingsobjectNoNoneInitial 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

NameTypeRequiredDefaultDescription
form_idintegerYesNoneThe ID of the form to add the field to.
typestringYesNoneThe 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

NameTypeRequiredDefaultDescription
form_idintegerYesNoneThe ID of the form that contains the field.
field_idintegerYesNoneThe 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

NameTypeRequiredDefaultDescription
form_idintegerYesNoneThe ID of the form to update.
settingsobjectYesNoneOne 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_types registers new field types. Each type declares its availability through a requires value, which is the string pro, an addon slug, or a callable, and can specify its required properties.
  • wpforms_integrations_abilities_field_registry_properties contributes 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;
    }
);