Skip to content
Home » Blog » Computer Science » Web Development » WordPress » How to add a Submission Counter to Contact Form 7

How to add a Submission Counter to Contact Form 7

3 minute read

One of the most frequent requests for Contact Form 7 (CF7) is the ability to generate a unique, sequential “ticket number” or “serial number” for every form submission. This is different from my GUID shortcode in Contact Form 7 – Dynamic Text Extension (DTX) because the GUID is a random string of letters and numbers, it doesn’t count or increment anything. Whether you are running a support desk or a contest, having a human-readable identifier for your submissions like entry-001 is much more useable and simpler for customers or clients to identify their receipt to you over the phone or email.

While CF7 doesn’t do this out of the box, there are a few ways to achieve it depending on your setup.

Option 1: Use a Form Submission Storage Plugin

If you are already using the Flamingo plugin to save your form submissions, you might not need any custom code at all.

As of Flamingo version 1.5+, you can use the [_serial_number] tag directly in your Contact Form 7 mail templates. This tag is automatically replaced by an incrementing numeric string for each submission. It’s simple, effective, and handles the counting for you.

Additionally, I am working on releasing a plugin soon that not only stores form submissions from Contact Form 7, but stores them as encrypted data to meet HIPAA regulations. This feature will absolutely be there too!

Option 2: PHP Code Snippet

If you need a very specific format (like including the current year or a custom prefix/suffix), you can implement your own counter using this small PHP snippet.

Step 1: Add a Placeholder to your Email Template

In the “Mail” tab of your Contact Form 7 settings, add an arbitrary placeholder where you want the number to appear. For example:

Ticket ID: [serial_number]

Note: Despite it looking like the other form tags, don’t add this as an input field in the “Form” tab. We are going to replace this text dynamically and directly in the email body content right before the email is sent after they click the send button. This ensures we only increment the counter for successful, non-spam submissions.

Step 2: Add the Custom Function

Add the following PHP code snippet to your website.

/**
 * Custom Form Submission Counter for Contact Form 7
 *
 * @param WPCF7_ContactForm $contact_form The current contact form.
 * @param bool $abort If the submission is being aborted.
 * @param WPCF7_Submission $submission The current submission data.
 *
 * @return void
 */
function au_cf7_submission_serial_number($contact_form, &$abort, $submission)
{
    // 1. Get the form's current count
    $count = (int)get_post_meta($contact_form->id(), '_submission_count', true);

    // 2. Increment the count
    $count++;

    // 3. Save the incremented count back to the form
    update_post_meta($contact_form->id(), '_submission_count', $count);

    // 4. Format the serial number (e.g., "ticket-2026-001")
    $serial = sprintf('ticket-%s-%03d', date('Y'), $count);

    // 5. Replace the placeholder in the primary email body
    $mail = $contact_form->prop('mail'); 
    $mail['body'] = str_replace('[serial_number]', $serial, $mail['body']);

    // 6. Replace the placeholder in "Mail (2)" if it's active
    $mail2 = $contact_form->prop('mail_2');
    if ($mail2['active']) {
        $mail2['body'] = str_replace('[serial_number]', $serial, $mail2['body']);
    }

    // 7. Save the updated mail properties back to the form instance
    $contact_form->set_properties(array(
        'mail' => $mail,
        'mail_2' => $mail2
    ));
}
add_action('wpcf7_before_send_mail', 'au_cf7_submission_serial_number', 10, 3);

This script works by storing the current count in the database (as post meta for that specific form), incrementing it with each successful submission, and then formatting it into a string like support-2026-001.

How It Works

  1. Database Storage: The code uses get_post_meta and update_post_meta on the form object to keep track of how many successful submissions that specific form has had. This means each form will have it’s own, independent counter, it’s not site-wide or page-specific.
  2. Custom Formatting: The sprintf function allows you to pad the number with zeros for a consistent look: %03d turns 1 into 001.
  3. Late Replacement: By using the wpcf7_before_send_mail hook, we ensure the number only increases if the form passes validation and isn’t caught by a spam filter.

Important Considerations

Before you go live, keep these two things in mind:

  • Simultaneous Submissions: If two people hit “Submit” at the exact same millisecond, there is a very small chance they could receive the same serial number. For most sites, this isn’t an issue, but for high-traffic sites, a more robust database locking mechanism might be needed.
  • Data Persistence: This code only puts the number in the email, so if you’re using a separate plugin for storing form submissions or an integration to pass on the submitted data elsewhere, it will not be available. This is fine if you’re only using it in emails, but if you need it as a reference number somewhere, you’ll need a solution to generate it and save it alongside the data as well.

Using this method gives you total control over your form’s serial numbers without the bloat of a heavy plugin!

Related Post Module Attributes Before

array(29) {
  ["post_type"]=>
  bool(false)
  ["post_id"]=>
  string(5) "23119"
  ["exclude"]=>
  string(2) "on"
  ["title"]=>
  string(27) "You might also like…"
  ["description"]=>
  string(0) ""
  ["max"]=>
  string(1) "4"
  ["post_ids"]=>
  string(0) ""
  ["exclude_ids"]=>
  string(0) ""
  ["is_series"]=>
  string(0) ""
  ["featured_term"]=>
  string(0) ""
  ["exclude_terms"]=>
  string(0) ""
  ["exclusive"]=>
  string(0) ""
  ["order"]=>
  string(4) "DESC"
  ["show_image"]=>
  string(2) "on"
  ["image_size"]=>
  string(6) "medium"
  ["menu_order_label"]=>
  string(0) ""
  ["show_order_label"]=>
  string(2) "on"
  ["show_date"]=>
  string(2) "on"
  ["show_meta_keys"]=>
  string(2) "on"
  ["show_modified"]=>
  string(0) ""
  ["show_author"]=>
  string(0) ""
  ["show_categories"]=>
  string(0) ""
  ["show_primary_category"]=>
  string(0) ""
  ["show_description"]=>
  string(0) ""
  ["show_reading_time"]=>
  string(2) "on"
  ["show_cta"]=>
  string(2) "on"
  ["cta"]=>
  string(9) "Read more"
  ["autoplay"]=>
  string(0) ""
  ["allow_sticky"]=>
  string(0) ""
}

Related Post Module Attributes

array(29) {
  ["post_type"]=>
  bool(false)
  ["post_id"]=>
  string(5) "23119"
  ["exclude"]=>
  string(2) "on"
  ["title"]=>
  string(27) "You might also like…"
  ["description"]=>
  string(0) ""
  ["max"]=>
  string(1) "4"
  ["post_ids"]=>
  string(0) ""
  ["exclude_ids"]=>
  string(0) ""
  ["is_series"]=>
  string(0) ""
  ["featured_term"]=>
  string(0) ""
  ["exclude_terms"]=>
  string(0) ""
  ["exclusive"]=>
  string(0) ""
  ["order"]=>
  string(4) "DESC"
  ["show_image"]=>
  string(2) "on"
  ["image_size"]=>
  string(6) "medium"
  ["menu_order_label"]=>
  string(0) ""
  ["show_order_label"]=>
  string(2) "on"
  ["show_date"]=>
  string(2) "on"
  ["show_meta_keys"]=>
  string(2) "on"
  ["show_modified"]=>
  string(0) ""
  ["show_author"]=>
  string(0) ""
  ["show_categories"]=>
  string(0) ""
  ["show_primary_category"]=>
  string(0) ""
  ["show_description"]=>
  string(0) ""
  ["show_reading_time"]=>
  string(2) "on"
  ["show_cta"]=>
  string(2) "on"
  ["cta"]=>
  string(9) "Read more"
  ["autoplay"]=>
  string(0) ""
  ["allow_sticky"]=>
  string(0) ""
}

Nobody has commented on this yet, be the first!

Your email address will not be published. Required fields are marked *