Docs & Support

Learn about all the Formidable features and get support from our amazing customer success team.

Database Schema

Formidable Forms saves forms, fields, entries, and payments in its own database tables. This database schema reference lists the columns in each table and shows how the tables connect, so you can query your form data directly and find data to clean up.

The table names below use the default wp_ table prefix. If your site uses a different prefix, replace wp_ with yours. In PHP, use $wpdb->prefix.
Formidable Forms uses custom post types in the wp_posts table. They are frm_form_actions for Form Actions, frm_styles for Form Styles, frm_display for Views, and frm_logs for Logs. Views are added by the Formidable Views add-on, and Logs by the Logs add-on.

Table relationships

Each form has fields and entries. Each entry has a row for every saved field value. These are the columns that link the tables:

ColumnPoints toWhat it links
wp_frm_fields.form_idwp_frm_forms.idThe form a field belongs to.
wp_frm_items.form_idwp_frm_forms.idThe form an entry was submitted in.
wp_frm_item_metas.item_idwp_frm_items.idThe entry a saved value belongs to.
wp_frm_item_metas.field_idwp_frm_fields.idThe field a saved value belongs to.
wp_frm_items.user_idwp_users.IDThe user who owns the entry.
wp_frm_items.post_idwp_posts.IDThe post that a Create Post action made from the entry.
wp_frm_items.parent_item_idwp_frm_items.idThe parent entry of a Repeater row or an embedded form entry.
wp_frm_forms.parent_form_idwp_frm_forms.idThe form that a Repeater's own form sits in.
wp_frm_payments.item_idwp_frm_items.idThe entry a payment was made for.
wp_frm_payments.action_idwp_posts.IDThe payment form action (a frm_form_actions post).
wp_frm_payments.sub_idwp_frm_subscriptions.idThe subscription a recurring payment belongs to.
wp_frm_subscriptions.item_idwp_frm_items.idThe entry a subscription was made for.

These are logical relationships. The tables don't define foreign keys, so the database doesn't enforce them. Deleting a row directly in the database doesn't delete the rows that point to it.

To delete an entry, use the Formidable → Entries page or FrmEntry::destroy(), which deletes the entry and its saved values. With Formidable Forms Pro, it also deletes the entry's Repeater rows and embedded form entries, and moves the post made from it to the trash. The entry's payment rows stay in wp_frm_payments.

Formidable Forms makes advanced site building simple. Launch forms, directories, dashboards, and custom WordPress apps faster than ever before.

wp_frm_forms

This table has a row for each form.

ColumnTypeWhat it stores
idINT(11) auto_incrementThe form ID.
form_keyVARCHAR(100)The unique form key.
nameVARCHAR(255)The form title.
descriptionTEXTThe form description.
parent_form_idINT(11)For the form that holds a Repeater's fields, the ID of the form the Repeater is in. Otherwise 0.
logged_inTINYINT(1)1 when Limit form visibility is on.
editableTINYINT(1)1 when Allow front-end editing of entries is on.
is_templateTINYINT(1)1 for a form saved as a template.
default_templateTINYINT(1)Used by the templates of older versions.
statusVARCHAR(255)published, draft, or trash.
optionsLONGTEXTThe form settings, serialized.
created_atDATETIMEWhen the form was created, in UTC.

wp_frm_fields

This table has a row for each field. Each field ID is unique across all forms.

ColumnTypeWhat it stores
idBIGINT(20) auto_incrementThe field ID.
field_keyVARCHAR(100)The unique field key.
nameTEXTThe field label.
descriptionLONGTEXTThe field description.
typeTEXTThe field type, such as text, email, or checkbox.
default_valueLONGTEXTThe default value.
optionsLONGTEXTThe choices of a field with options, such as Checkboxes or Dropdown, serialized.
field_orderINT(11)The field's position in the form.
requiredINT(1)1 when the field is required.
field_optionsLONGTEXTThe field settings, serialized. For a Repeater or Embed Form field, form_select holds the ID of the form with its fields.
form_idINT(11)The ID of the form the field belongs to.
created_atDATETIMEWhen the field was created, in UTC.

wp_frm_items

This table has a row for each entry. Field values aren't in this table: they're in wp_frm_item_metas.

ColumnTypeWhat it stores
idBIGINT(20) auto_incrementThe entry ID.
item_keyVARCHAR(100)The unique entry key.
nameVARCHAR(255)The entry name.
descriptionTEXTThe submitter's browser and referring page.
ipTEXTThe submitter's IP address. Empty when Do not store user IPs with form submissions is on.
form_idBIGINT(20)The ID of the form the entry was submitted in.
post_idBIGINT(20)The ID of the post a Create Post action made from the entry. Otherwise 0.
user_idBIGINT(20)The ID of the user who owns the entry. 0 for a logged-out visitor.
parent_item_idBIGINT(20)For a Repeater row or an embedded form entry, the ID of the parent entry. Otherwise 0.
is_draftTINYINT(1)The entry status: 0 submitted, 1 draft, 4 spam. The Form Abandonment add-on adds 2 in progress and 3 abandoned.
updated_byBIGINT(20)The ID of the user who last saved the entry.
created_atDATETIMEWhen the entry was created, in UTC.
updated_atDATETIMEWhen the entry was last updated, in UTC.

wp_frm_item_metas

This table has a row for each field value saved in an entry.

ColumnTypeWhat it stores
idBIGINT(20) auto_incrementThe row ID.
meta_valueLONGTEXTThe saved value. A field with several values, such as Checkboxes, saves a serialized array.
field_idBIGINT(20)The ID of the field the value belongs to.
item_idBIGINT(20)The ID of the entry the value belongs to.
created_atDATETIMEWhen the value was saved, in UTC.

A row with field_id 0 holds entry details that don't belong to a field, such as the ID used to detect duplicate submissions. In Formidable Forms Pro, these rows also hold entry comments.

wp_frm_payments

This table has a row for each payment made through a payment form action, such as Stripe, Square, or PayPal.

ColumnTypeWhat it stores
idBIGINT(20) NOT NULL auto_incrementThe payment ID.
meta_valueLONGTEXT default NULLPayment details, serialized.
receipt_idVARCHAR(100) default NULLThe payment ID from the payment gateway.
invoice_idVARCHAR(100) default NULLThe invoice ID from the payment gateway.
sub_idVARCHAR(100) default NULLFor a recurring payment, the id of its row in wp_frm_subscriptions.
item_idBIGINT(20) NOT NULLThe ID of the entry the payment was made for.
action_idBIGINT(20) NOT NULLThe ID of the payment form action.
amountDECIMAL(12,2) NOT NULL default '0.00'The amount.
statusVARCHAR(100) default NULLThe payment status, such as complete or failed.
begin_dateDATE NOT NULLThe payment date.
expire_dateDATE default NULLThe date the payment period ends, for a recurring payment.
paysysVARCHAR(100) default NULLThe payment gateway, such as stripe.
created_atDATETIME NOT NULLWhen the row was created, in UTC.
testTINYINT(1) default NULL1 for a payment made in test mode.

A site that had the PayPal Standard add-on active at any time also has a completed column (0 or 1), which that add-on created. Formidable Forms 7.0 doesn't create or use it, except to copy old values into status when it updates the table.

wp_frm_subscriptions

This table has a row for each recurring payment subscription.

ColumnTypeWhat it stores
idBIGINT(20) NOT NULL auto_incrementThe subscription ID. Payments for this subscription have this value in sub_id.
sub_idVARCHAR(100) default NULLThe subscription ID from the payment gateway.
meta_valueLONGTEXT default NULLSubscription details, serialized.
item_idBIGINT(20) NOT NULLThe ID of the entry the subscription was made for.
action_idBIGINT(20) NOT NULLThe ID of the payment form action.
amountDECIMAL(12,2) NOT NULL default '0.00'The amount of each payment.
first_amountDECIMAL(12,2) NOT NULL default '0.00'The first payment amount, when it differs from the recurring amount. Usually 0.00.
interval_countBIGINT(20) default 1How many intervals pass between payments.
time_intervalVARCHAR(100) default NULLThe interval, such as month or year.
fail_countBIGINT(20) default 0The number of failed payments.
end_countBIGINT(20) default NULLThe number of payments after which the subscription ends, when set.
next_bill_dateDATE default NULLThe date of the next payment.
statusVARCHAR(100) default NULLThe subscription status, such as active or canceled.
paysysVARCHAR(100) default NULLThe payment gateway, such as stripe.
created_atDATETIME NOT NULLWhen the row was created, in UTC.
testTINYINT(1) default NULL1 for a subscription made in test mode.

Other tables

Some features add tables of their own:

  • wp_frm_gated_tokens: the access tokens of gated content. entry_id points to wp_frm_items.id and action_id to the form action.
  • wp_frm_pro_meta_index: an index of short field values that Formidable Forms Pro uses to speed up Lookup fields and Views. Formidable builds it from wp_frm_item_metas, which stays the source of the values, so don't edit it.
  • wp_frm_view_layouts: the listing and detail layouts built in the Views layout builder, added by the Formidable Views add-on. view_id points to the View's post ID.
  • wp_frmpro_copies: on a multisite network, the forms and Views set to be copied to the other sites.

Repeater and embedded form entries

A Repeater field's fields are in a form of their own, whose parent_form_id is the ID of the main form. An Embed Form field shows another form, which keeps parent_form_id 0.

When an entry is saved, each Repeater row and each embedded form entry is saved as its own entry in wp_frm_items. Its parent_item_id is the main entry's ID. In the main entry, the Repeater or Embed Form field's value in wp_frm_item_metas is a serialized list of those entry IDs.

Find a form's entries

This query lists the submitted entries of a form, with each saved value and its field label. Replace 5 with your form ID. To find it, go to Formidable → Forms and check the ID column.

SELECT e.id AS entry_id, e.created_at, f.name AS field_name, m.meta_value
FROM wp_frm_items e
INNER JOIN wp_frm_item_metas m ON m.item_id = e.id
INNER JOIN wp_frm_fields f ON f.id = m.field_id
WHERE e.form_id = 5
AND e.is_draft = 0
ORDER BY e.id, f.field_order;

To use the form key instead of the form ID, join wp_frm_forms. Replace contact-form with your form key.

SELECT e.id AS entry_id, e.created_at
FROM wp_frm_items e
INNER JOIN wp_frm_forms fo ON fo.id = e.form_id
WHERE fo.form_key = 'contact-form'
AND e.is_draft = 0
ORDER BY e.created_at DESC;

To list the Repeater rows of an entry with their values, look up the entries whose parent_item_id is that entry's ID. Replace 25 with the entry ID.

Embedded form entries use parent_item_id too. To skip them, the query joins wp_frm_forms and keeps rows from a form with a parent_form_id, which only a Repeater's form has.

SELECT r.id AS row_entry_id, f.name AS field_name, m.meta_value
FROM wp_frm_items r
INNER JOIN wp_frm_item_metas m ON m.item_id = r.id
INNER JOIN wp_frm_fields f ON f.id = m.field_id
INNER JOIN wp_frm_forms fo ON fo.id = r.form_id
WHERE r.parent_item_id = 25
AND fo.parent_form_id <> 0
ORDER BY r.id, f.field_order;

To list the payments for a form's entries, join wp_frm_payments on item_id. Replace 5 with your form ID.

SELECT e.id AS entry_id, p.amount, p.status, p.paysys, p.begin_date
FROM wp_frm_payments p
INNER JOIN wp_frm_items e ON e.id = p.item_id
WHERE e.form_id = 5
ORDER BY p.begin_date DESC;
Learn where to put the code when adding a custom PHP function to your site.

In PHP, the same query runs through $wpdb, with the table prefix and the form ID filled in for you:

global $wpdb;

$form_id = 5; // Replace 5 with your form ID.
$rows    = $wpdb->get_results(
	$wpdb->prepare(
		"SELECT e.id AS entry_id, f.name AS field_name, m.meta_value
		FROM {$wpdb->prefix}frm_items e
		INNER JOIN {$wpdb->prefix}frm_item_metas m ON m.item_id = e.id
		INNER JOIN {$wpdb->prefix}frm_fields f ON f.id = m.field_id
		WHERE e.form_id = %d AND e.is_draft = 0
		ORDER BY e.id, f.field_order",
		$form_id
	)
);

A value from a field with several values comes back serialized. To get it as an array, use maybe_unserialize() on meta_value, or get the values with the PHP functions in get field values as an array.

Clean up the database

When the Formidable tables get large, delete the data you no longer need with the built-in tools. Before you delete anything, back up your database and your site files, and make sure the backup restores. A form or post in the trash can be restored from there, but data that's deleted for good comes back only from a backup.

Delete data with the built-in tools

  • Delete the entries you no longer need from the form's Entries page. Learn how to bulk delete entries. Bulk deleting entries is a premium feature.
  • Delete entries after a set number of days with Automatically delete entries in the form's GDPR settings. Deleting entries automatically is a premium feature.
  • Delete old spam with Delete spam entries automatically in the Captcha/Spam global settings. Deleting spam automatically is a premium feature.

Check what else goes with an entry before you delete it. Deleting an entry from its row, or deleting selected entries in bulk, also deletes:

  • Its saved values, its Repeater rows, and its embedded form entries.
  • The post linked to it, such as one a Create Post action made. Posts and pages go to the trash. They're deleted for good when they're already in the trash, when your site turns off the WordPress trash, or when they're another post type.
  • Its uploaded files, when the File Upload field is set to delete files with the entry. A file is deleted from the Media Library even if you use it somewhere else on your site.

It also runs the form actions set to run when an entry is deleted, and any custom code or other plugin that runs when an entry is deleted.

The Delete All Entries button works differently. It deletes the linked posts and files and runs those actions only when the form has a Create Post action, or when you check Trigger all actions that happen "on entry deleted". Learn more about deleting all entries.

Deleting a field deletes the values saved in that field, in every entry. Deleting a Repeater field also deletes all of its Repeater rows.

Permanently deleting a form deletes its fields, entries, saved values, and form actions. Its entries go the same way as entries deleted from their row, with their linked posts and files. It also deletes the Views and landing pages made for it. A form you delete goes to the trash first and, by default, is deleted for good after 30 days. If your site turns off the WordPress trash, the form is deleted at once. To delete trashed forms sooner, go to Formidable → Forms, open the Trash tab, and click Empty Trash. This deletes every form in the trash.

The Logs add-on saves its logs in wp_posts and wp_postmeta, not in the Formidable tables. To delete old logs, go to Formidable → Global Settings → Logs and turn on Automatically delete log entries every 30 days. When the cleanup runs, about once a month, it deletes the logs older than one week by default.

Find leftover data

For developers and database experts only. The queries below only read your database. They don't change or delete anything. Removing the rows or tables they find can't be undone, and done wrong, it can delete Formidable data or another plugin's data. We offer these queries as a courtesy, but don't provide support for running them or for removing data from your database.

Run the queries in phpMyAdmin, in your host's database tool, or with wp db query. Replace every wp_ in a query with your table prefix, including the ones inside quotes. The prefix is the $table_prefix value in wp-config.php. On a multisite network, use the prefix of the site you're checking. The main site uses the plain prefix, and the other sites add their site ID, such as wp_3_.

To see which Formidable tables are the largest, with their size in megabytes:

SELECT table_name, table_rows,
ROUND((data_length + index_length) / 1024 / 1024, 1) AS size_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name IN ('wp_frm_forms', 'wp_frm_fields', 'wp_frm_items', 'wp_frm_item_metas', 'wp_frm_payments', 'wp_frm_subscriptions', 'wp_frm_gated_tokens', 'wp_frm_pro_meta_index', 'wp_frm_view_layouts')
ORDER BY data_length + index_length DESC;

On InnoDB tables, table_rows and the size are estimates.

Formidable deletes an entry's values and Repeater rows with the entry, and a field's values with the field. Rows deleted directly in the database, such as in phpMyAdmin, can leave these behind. These queries count them.

Entries whose form or parent entry no longer exists, such as the Repeater rows of an entry deleted directly:

SELECT COUNT(*)
FROM wp_frm_items e
LEFT JOIN wp_frm_forms fo ON fo.id = e.form_id
LEFT JOIN wp_frm_items p ON p.id = e.parent_item_id
WHERE fo.id IS NULL
OR (e.parent_item_id <> 0 AND p.id IS NULL);

Values whose entry no longer exists:

SELECT COUNT(*)
FROM wp_frm_item_metas m
LEFT JOIN wp_frm_items e ON e.id = m.item_id
WHERE e.id IS NULL;

Values whose field no longer exists. These values can belong to entries that still exist. The query skips rows with field_id 0, because they hold entry details, not field values.

SELECT COUNT(*)
FROM wp_frm_item_metas m
LEFT JOIN wp_frm_fields f ON f.id = m.field_id
WHERE m.field_id <> 0
AND f.id IS NULL;

A count only shows that a link between rows is missing. It doesn't prove the rows aren't needed. The counts can also include rows from an import that didn't finish. Have a developer check that import on a staging copy before anything is removed.

Find the tables of deleted network sites

On a multisite network, each site other than the main site has its own Formidable tables, with the site ID in the table prefix, such as wp_3_frm_items. When you delete a site from the network, some of its Formidable tables can stay in the database. When Formidable isn't loaded while the site is deleted, such as when it's active only on that site, none of them are removed.

This query lists the Formidable tables whose site ID is no longer in wp_blogs and whose site's options table is gone too. Sites that are archived, marked as spam, or flagged for deletion are still in wp_blogs, so their tables aren't listed. Use your network's main table prefix in place of wp_.

SELECT t.table_name
FROM information_schema.tables t
LEFT JOIN wp_blogs b
ON CAST(b.blog_id AS CHAR) = SUBSTRING_INDEX(SUBSTRING(t.table_name, LENGTH('wp_') + 1), '_', 1)
WHERE t.table_schema = DATABASE()
AND t.table_name REGEXP '^wp_[0-9]+_frm_(fields|forms|items|item_metas|payments|subscriptions|gated_tokens|pro_meta_index|view_layouts)$'
AND b.blog_id IS NULL
AND NOT EXISTS (
SELECT 1 FROM information_schema.tables o
WHERE o.table_schema = DATABASE()
AND o.table_name = CONCAT('wp_', SUBSTRING_INDEX(SUBSTRING(t.table_name, LENGTH('wp_') + 1), '_', 1), '_options')
)
ORDER BY t.table_name;

A listed table may belong to a deleted site. Before anyone removes it, make sure it belongs to this network and not to another site that shares the database. Also make sure the site was deleted on purpose and that you don't need its data. The payments and subscriptions tables can hold records you need to keep.

Remove leftover data

To remove the rows or tables these queries find, or to make a large table smaller on disk, ask your developer or your host. Before they remove anything, they should:

  • Check each row or table and what depends on it. Repair or recover data that's still needed instead of removing it.
  • Try the cleanup on a staging copy of the site first.
  • Stop anything that writes to the database, such as form submissions, imports, and scheduled tasks. Then make a full backup of the database and the site files, and test that it restores.
  • Run the queries again right before the cleanup, and keep a copy of the exact rows or tables they remove.
Was this article helpful? *

This article may contain affiliate links. Once in a while, we earn commissions from those links. But we only recommend products we like, with or without commissions.

In this article

    We have a small, but amazing team of dedicated people who are committed to helping you achieve your goals and project requirements.


    Copyright © 2026 Strategy11, LLC. Formidable Forms® is a registered trademark Strategy11, LLC.

    Complete your purchase
    Don't forget your purchase!
    Complete Purchase
    Join 400,000+ using Formidable Forms to create form-focused solutions fast. Get Formidable Forms