<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rahul Sharma</title>
    <description>The latest articles on DEV Community by Rahul Sharma (@rahul_sharma_15bd129bc69e).</description>
    <link>https://dev.to/rahul_sharma_15bd129bc69e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3476785%2F7115035a-75f6-4d9a-926e-0da2af1b4b40.png</url>
      <title>DEV Community: Rahul Sharma</title>
      <link>https://dev.to/rahul_sharma_15bd129bc69e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rahul_sharma_15bd129bc69e"/>
    <language>en</language>
    <item>
      <title>CF7 to Monday.com Item Not Appearing in Board: A Complete Troubleshooting Guide</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Thu, 12 Nov 2026 06:20:41 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-mondaycom-item-not-appearing-in-board-a-complete-troubleshooting-guide-48fb</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-mondaycom-item-not-appearing-in-board-a-complete-troubleshooting-guide-48fb</guid>
      <description>&lt;p&gt;You have connected Contact Form 7 to Monday.com through a connector plugin. The API logs show a successful 200 OK response. The item ID is returned. Everything looks correct.&lt;/p&gt;

&lt;p&gt;But when you open the board in Monday.com, the item is not there.&lt;/p&gt;

&lt;p&gt;This is one of the most confusing integration failures because the API tells you it succeeded. The request was valid. The item was created. But it is invisible to you.&lt;/p&gt;

&lt;p&gt;This guide explains exactly why this happens and how to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Monday.com Says "Success" When the Item Is Invisible
&lt;/h2&gt;

&lt;p&gt;Monday.com's API creates items at the account level, not at the board level. The &lt;code&gt;create_item&lt;/code&gt; mutation accepts a &lt;code&gt;board_id&lt;/code&gt; and returns the created item's ID and URL. But the item's visibility depends on two factors that the API response does not validate:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission scope of the API token.&lt;/strong&gt; If the API token belongs to a user who does not have access to the board, or if the token's permissions are limited, the item is created but is not visible to anyone viewing the board through a different user account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Column ID and value format.&lt;/strong&gt; If the &lt;code&gt;columnValues&lt;/code&gt; payload contains incorrect column IDs or value formats, Monday may accept the request but silently ignore the column values, creating an item with empty fields that does not appear in filtered views.&lt;/p&gt;

&lt;p&gt;The first is the most common cause. A Monday.com community response confirmed this directly: "The solution was a permission issue. The API key used wasn't from the same user which I was using to view the board. The solution is to add permissions or create an API Key from the user I want to see the items."&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #1: The API Token Belongs to a Different User
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; The API returns a successful response with an item ID. The item URL works when accessed directly. But the board view does not show the item.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Monday.com's API token is tied to a specific user account. If you generated the token from User A, but you are viewing the board as User B, and User B does not have permission to see items created by User A, the item is invisible in your view. This is not a bug. It is how Monday's permission model works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A: Generate the API token from the user who will view the board.&lt;/strong&gt; If your team views the board as &lt;code&gt;admin@company.com&lt;/code&gt;, generate the API token while logged in as &lt;code&gt;admin@company.com&lt;/code&gt;. This ensures the item is created under the same account context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B: Grant the token's user access to the board.&lt;/strong&gt; If you must use a service account token, add that service account as a board member with edit permissions. Navigate to the board, click "Invite," and add the user associated with the API token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to verify:&lt;/strong&gt; After generating a new token from the correct user, submit a test form. Check the board while logged in as that same user. The item should appear.&lt;/p&gt;

&lt;p&gt;If no request appears in the plugin's logs at all, the problem is earlier in the pipeline. For a deeper breakdown of that scenario, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/" rel="noopener noreferrer"&gt;Contact Form 7 data not reaching the API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #2: The Column IDs in the Payload Are Wrong
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; The item appears in the board, but all column values are empty. Or the item is created in the wrong group and does not appear in the filtered view you are checking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Monday's &lt;code&gt;create_item&lt;/code&gt; mutation requires column IDs, not column display names. If your payload uses &lt;code&gt;"status": {"label": "Done"}&lt;/code&gt; but the actual column ID is &lt;code&gt;"status_mm37gbqg"&lt;/code&gt;, Monday ignores the value.&lt;/p&gt;

&lt;p&gt;The same applies to group IDs. If you do not specify a &lt;code&gt;group_id&lt;/code&gt;, the item goes to the board's top group. If you are checking a different group, the item appears to be missing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Retrieve the correct column IDs.&lt;/strong&gt; Use Monday's &lt;code&gt;get_board_info&lt;/code&gt; tool or query the board via GraphQL to list all columns and their IDs. The column ID is not the same as the column title.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Update your JSON payload.&lt;/strong&gt; For a status column, the format is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"status_mm37gbqg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Working on it"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a text column, the format is a plain string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"text_col"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a date column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"date_col"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2024-05-10"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Specify the group ID if needed.&lt;/strong&gt; If you want the item in a specific group, add &lt;code&gt;groupId&lt;/code&gt; to your payload. Otherwise, it goes to the top group.&lt;/p&gt;

&lt;p&gt;For a deeper dive into JSON structure mapping and column value formatting, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #3: The Payload Structure Is Wrong for Monday's GraphQL API
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Monday returns a 400 Bad Request error, or the item is created with no name and no column values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Monday.com uses GraphQL, not REST. The connector plugin may be configured to send a REST-style JSON body, which Monday's API does not accept. The &lt;code&gt;create_item&lt;/code&gt; mutation expects a specific GraphQL structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Monday's GraphQL &lt;code&gt;create_item&lt;/code&gt; mutation requires three parameters at minimum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;board_id&lt;/code&gt;: The numeric ID of the board&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;item_name&lt;/code&gt;: The name of the item (a plain string)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;column_values&lt;/code&gt;: A JSON string containing column ID to value mappings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your payload sent through the plugin should be structured as a GraphQL query. If your connector plugin supports raw JSON payloads, the body must include the &lt;code&gt;query&lt;/code&gt; and &lt;code&gt;variables&lt;/code&gt; fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"mutation ($boardId: ID!, $itemName: String!, $columnValues: JSON) { create_item(board_id: $boardId, item_name: $itemName, column_values: $columnValues) { id } }"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"variables"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;"boardId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18412502536&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"itemName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"[your-name] - Website Inquiry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;"columnValues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"{\"status_mm37gbqg\": {\"label\": \"New\"}}"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical detail:&lt;/strong&gt; The &lt;code&gt;columnValues&lt;/code&gt; parameter is a JSON string nested inside the GraphQL variables. This "JSON-in-JSON" pattern is specific to Monday's API and is the source of many payload errors.&lt;/p&gt;

&lt;p&gt;If your connector plugin does not support GraphQL queries, you may need to use a plugin that does, or configure a middleware service to translate the request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #4: The Board ID Is Wrong or the Token Lacks Board Access
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Monday returns a 404 Not Found error, or a 403 Forbidden error. No item is created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; The &lt;code&gt;board_id&lt;/code&gt; in your payload must be the numeric ID of the target board. If you use the board's URL slug instead of its ID, the request fails. Additionally, the API token must have access to that specific board.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Get the correct board ID.&lt;/strong&gt; Open the board in Monday.com. The URL contains the board ID: &lt;code&gt;https://yourcompany.monday.com/boards/18412502536&lt;/code&gt;. The numeric portion is the board ID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Verify token access.&lt;/strong&gt; In Monday.com, navigate to the board and check the board's members. The user associated with the API token must be listed as a member with at least edit permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Test with a minimal payload.&lt;/strong&gt; Hardcode the &lt;code&gt;board_id&lt;/code&gt; and &lt;code&gt;item_name&lt;/code&gt; values. Submit a test form. If the item appears, the issue is column values or permissions. If it still fails, the token or board ID is wrong.&lt;/p&gt;

&lt;p&gt;For a comprehensive breakdown of API authentication errors and how they appear in logs, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Checklist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Reveals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Check the plugin's API Logs&lt;/td&gt;
&lt;td&gt;Was a request sent? What response code and body?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Verify the API token's user&lt;/td&gt;
&lt;td&gt;Is it the same user viewing the board?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Check board membership&lt;/td&gt;
&lt;td&gt;Is the token's user a member of the board?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Verify column IDs&lt;/td&gt;
&lt;td&gt;Are they the IDs from &lt;code&gt;get_board_info&lt;/code&gt;, not display names?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Check the payload structure&lt;/td&gt;
&lt;td&gt;Is it a GraphQL query, not a REST body?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Test with minimal payload&lt;/td&gt;
&lt;td&gt;Hardcode &lt;code&gt;board_id&lt;/code&gt; and &lt;code&gt;item_name&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Beyond the Board: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your Monday.com integration works, you might want to send the same submission to other tools: a CRM, an email marketing platform, or a help desk system.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can create a Monday item, add a HubSpot contact, and send an SMS via Twilio, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The API can return success while the item is invisible.&lt;/strong&gt; Monday's &lt;code&gt;create_item&lt;/code&gt; response does not validate visibility. The item is created at the account level, and its visibility depends on the API token's user permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generate the API token from the user who views the board.&lt;/strong&gt; If your team views the board as &lt;code&gt;admin@company.com&lt;/code&gt;, create the token while logged in as that user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Column IDs must be exact.&lt;/strong&gt; Use &lt;code&gt;get_board_info&lt;/code&gt; to retrieve the correct IDs. Display names do not work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monday uses GraphQL, not REST.&lt;/strong&gt; The payload must be a GraphQL query with &lt;code&gt;columnValues&lt;/code&gt; as a JSON string nested inside the variables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with a minimal payload.&lt;/strong&gt; Hardcode &lt;code&gt;board_id&lt;/code&gt; and &lt;code&gt;item_name&lt;/code&gt;. Once the item appears, add column values one at a time.&lt;/p&gt;

&lt;p&gt;With the correct API token, verified board permissions, and a properly structured GraphQL payload, your CF7 submissions will create visible items in your Monday.com board reliably.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>mondaycrm</category>
      <category>apiintegration</category>
    </item>
    <item>
      <title>CF7 to HubSpot Deal Not Created: Why It Happens and How to Fix It</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Fri, 06 Nov 2026 12:24:16 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-hubspot-deal-not-created-why-it-happens-and-how-to-fix-it-3omc</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-hubspot-deal-not-created-why-it-happens-and-how-to-fix-it-3omc</guid>
      <description>&lt;p&gt;You have connected Contact Form 7 to HubSpot. Contacts appear in your CRM. Companies are created. But deals? They never show up.&lt;/p&gt;

&lt;p&gt;This is one of the most common frustrations with CF7-to-HubSpot integrations. The contact gets created. The deal does not. And the reason is not a bug in your plugin or a missing field mapping. It is a fundamental architectural fact about how HubSpot works.&lt;/p&gt;

&lt;p&gt;HubSpot's form submission APIs are &lt;strong&gt;contact-centric&lt;/strong&gt;. They are designed to create or update contacts. They do not create deals. Even if you include deal fields in a form submission, HubSpot's API ignores them.&lt;/p&gt;

&lt;p&gt;This guide explains exactly why deals fail to create and walks through the verified solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why HubSpot Does Not Create Deals from Form Submissions
&lt;/h2&gt;

&lt;p&gt;HubSpot's native form handling and its Forms API are built around contacts. When a form is submitted, HubSpot creates or updates a contact record. It may also create a company if the contact's email domain matches an existing company or if specific settings are enabled. But deals are not part of the default form submission flow.&lt;/p&gt;

&lt;p&gt;A HubSpot community moderator confirmed this directly: "Out of the box form submissions do not create deals." Even if you put deal fields on a form definition, the submissions endpoint is designed to create or update the contact record only, then expose those values so you can act on them with workflows or other APIs afterward.&lt;/p&gt;

&lt;p&gt;This means that regardless of which WordPress plugin you use "Contact Form to Any API," the official HubSpot plugin, or any other integration a standard form submission will not create a deal automatically.&lt;/p&gt;

&lt;p&gt;If no contact appears in HubSpot at all, the problem may be earlier in the pipeline. For a deeper breakdown of that scenario, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/" rel="noopener noreferrer"&gt;Contact Form 7 data not reaching the API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #1: You Are Targeting the Contacts Endpoint, Not the Deals Endpoint
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Contacts appear in HubSpot. Deals never appear. The plugin's API logs show a successful 200 or 201 response, because the contact creation succeeded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; The "Contact Form to Any API" plugin requires you to specify the API endpoint manually. If you are using the HubSpot Contacts endpoint (&lt;code&gt;/crm/v3/objects/contacts&lt;/code&gt;), the plugin creates a contact and nothing else. There is no deal creation because the endpoint does not handle deals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You have two options. The first is to target the Deals endpoint directly. HubSpot's Deals API accepts a POST request to &lt;code&gt;/crm/v3/objects/deals&lt;/code&gt; with a properties object.&lt;/p&gt;

&lt;p&gt;Your payload would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dealname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-name] - Website Inquiry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dealstage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"appointmentscheduled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical detail:&lt;/strong&gt; You must use the &lt;strong&gt;internal ID&lt;/strong&gt; of a deal stage, not its display label. The internal ID for "Appointment Scheduled" might be &lt;code&gt;appointmentscheduled&lt;/code&gt;, but you should verify this in your HubSpot deal pipeline settings.&lt;/p&gt;

&lt;p&gt;The second option is to create the contact via the plugin and then use a HubSpot workflow to create the deal. This is the approach HubSpot recommends for most users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #2: The Deal Stage or Pipeline ID Is Wrong
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; HubSpot returns a 400 Bad Request error. The response mentions an invalid dealstage or pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; HubSpot's Deals API requires internal IDs for &lt;code&gt;dealstage&lt;/code&gt; and &lt;code&gt;pipeline&lt;/code&gt;, not human-readable names. If you use &lt;code&gt;dealstage: "New"&lt;/code&gt; but the internal ID is &lt;code&gt;appointmentscheduled&lt;/code&gt;, the request fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; In HubSpot, navigate to Settings → Objects → Deals → Pipelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Click on your pipeline and locate the stage you want to use. The internal ID is displayed alongside the stage name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Update your JSON payload to use the internal ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dealname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-name] - Website Inquiry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dealstage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"appointmentscheduled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you omit &lt;code&gt;pipeline&lt;/code&gt;, HubSpot uses the default pipeline. If you omit &lt;code&gt;dealstage&lt;/code&gt;, HubSpot may reject the request or use a default stage depending on your account configuration.&lt;/p&gt;

&lt;p&gt;For a deeper dive into JSON structure mapping and payload construction, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #3: The Workflow That Should Create Deals Is Not Firing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Contacts are created. The workflow you set up to create deals is either not enrolling contacts or not completing the deal creation action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; HubSpot workflows that create deals from form submissions require specific enrollment triggers and actions. If the enrollment criteria do not match the contact's properties, the workflow never runs. If the "Create record" action is missing or misconfigured, the deal is never created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; In HubSpot, navigate to Automation → Workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Check your deal-creation workflow. The enrollment trigger should be "Contact submitted form X" or "Contact property [specific field] is known."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Verify the workflow contains a "Create record" action targeting Deals. The action should map contact properties to deal properties.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Check the workflow's action log. If contacts are enrolling but the action fails, the log will show the error. If contacts are not enrolling, the enrollment criteria are too narrow.&lt;/p&gt;

&lt;p&gt;A HubSpot community user described this exact problem: contacts were created from form submissions, but deals were not. The moderator confirmed that a workflow was required and asked the user to check the action log for enrollment or execution errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #4: The Contact Has No Associated Company
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Deals are created for some contacts (those with business email domains) but not others (those with Gmail, Yahoo, or other free email providers).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; If your deal-creation workflow or HubSpot's automatic company association settings depend on the contact having a company, freemail contacts will not trigger deal creation. HubSpot's "Automatically create and associate companies with contacts" setting may block company creation for free email domains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A:&lt;/strong&gt; Create a second workflow that enrolls contacts without an associated company and creates the company first. Then the deal-creation workflow can run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B:&lt;/strong&gt; Modify your deal-creation workflow to not require a company association. The deal can be created directly from the contact record without a company link.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option C:&lt;/strong&gt; Use the "Contact Form to Any API" plugin to call the Deals API directly, bypassing HubSpot's workflow logic entirely. This gives you full control over when a deal is created, regardless of the contact's email domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Checklist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Reveals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Check the plugin's API Logs&lt;/td&gt;
&lt;td&gt;Was a request sent? To which endpoint?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Verify the endpoint URL&lt;/td&gt;
&lt;td&gt;Does it target &lt;code&gt;/deals&lt;/code&gt; or &lt;code&gt;/contacts&lt;/code&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Check the dealstage internal ID&lt;/td&gt;
&lt;td&gt;Is it the ID, not the display name?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Check HubSpot workflow enrollment&lt;/td&gt;
&lt;td&gt;Are contacts enrolling in the deal-creation workflow?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Check the workflow action log&lt;/td&gt;
&lt;td&gt;Did the "Create record" action execute successfully?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Test with a business email&lt;/td&gt;
&lt;td&gt;Does the deal create when the contact has a company domain?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a comprehensive breakdown of API authentication and error handling across different integrations, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Deals: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your deal creation works, you might want to send the same submission to other HubSpot objects or external tools.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can create a HubSpot contact, a deal, and send an SMS via Twilio, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;HubSpot form submissions do not create deals automatically.&lt;/strong&gt; The Forms API and contact endpoints are contact-centric. Deals require either a direct API call to the Deals endpoint or a HubSpot workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Deals API requires internal IDs for dealstage and pipeline.&lt;/strong&gt; Using display names instead of IDs causes 400 errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflows are the native HubSpot solution.&lt;/strong&gt; Create a workflow that enrolls contacts from form submissions and uses the "Create record" action to create deals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Freemail contacts may not trigger company-based workflows.&lt;/strong&gt; If your workflow requires a company association, Gmail and Yahoo contacts may be skipped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Direct API calls to the Deals endpoint give you full control.&lt;/strong&gt; Use the plugin to POST to &lt;code&gt;/crm/v3/objects/deals&lt;/code&gt; with the correct properties and IDs, bypassing HubSpot's workflow logic entirely.&lt;/p&gt;

&lt;p&gt;With the right endpoint, correct internal IDs, and a properly configured workflow or direct API call, your CF7 submissions will create deals in HubSpot reliably.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>hubspot</category>
      <category>apiintegration</category>
    </item>
    <item>
      <title>CF7 Form Data Validation Before Sending to API: A Complete Implementation Guide</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Mon, 02 Nov 2026 05:09:00 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-form-data-validation-before-sending-to-api-a-complete-implementation-guide-3ba9</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-form-data-validation-before-sending-to-api-a-complete-implementation-guide-3ba9</guid>
      <description>&lt;p&gt;You have a Contact Form 7 form connected to an external API. The form submits. The plugin fires the API request. And then the API rejects it—because the email domain is blocked, the phone number format is wrong for that specific service, or the field value violates a business rule the API enforces.&lt;/p&gt;

&lt;p&gt;The user sees a generic success message. The API log shows a failure. And you have no way to tell the user what went wrong before the request was even sent.&lt;/p&gt;

&lt;p&gt;This is the validation gap. CF7 validates what it knows: required fields, email format, file size. It does not know your API's rules. Bridging that gap requires custom validation that runs &lt;strong&gt;before&lt;/strong&gt; the API request fires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why CF7's Built-in Validation Is Not Enough
&lt;/h2&gt;

&lt;p&gt;Contact Form 7 provides default validation for common field types. A required field must be filled. An email field must contain a valid email address. A file upload must be within size limits .&lt;/p&gt;

&lt;p&gt;But API integrations have rules CF7 does not enforce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The email must belong to a business domain, not a free provider&lt;/li&gt;
&lt;li&gt;The phone number must match the format expected by the CRM&lt;/li&gt;
&lt;li&gt;The company name must exist in your ERP's customer database&lt;/li&gt;
&lt;li&gt;The budget field must be within a range the sales team can handle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the API rejects the submission, the user has already seen a success message (or the form hangs, depending on the plugin's response handling). The validation happened too late.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Validate Before the API Call Fires
&lt;/h2&gt;

&lt;p&gt;The key insight is that validation must happen at the CF7 submission stage, &lt;strong&gt;before&lt;/strong&gt; the connector plugin sends data to the API. CF7 provides hooks that fire during its own validation process, giving you a chance to inject custom rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 1: Custom Validation Filters (Legacy Method)
&lt;/h3&gt;

&lt;p&gt;CF7 implements validation as a filter. The hook name follows the pattern &lt;code&gt;wpcf7_validate_[field_type]&lt;/code&gt;. For a text field, it is &lt;code&gt;wpcf7_validate_text&lt;/code&gt;. For a required text field, it is &lt;code&gt;wpcf7_validate_text*&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;A validation filter receives two parameters: the result object and the form tag. You check the field value and call &lt;code&gt;$result-&amp;gt;invalidate()&lt;/code&gt; if it fails your custom rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wpcf7_validate_text*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'validate_business_email_domain'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validate_business_email_domain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;'your-email'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'your-email'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;sanitize_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'your-email'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$free_providers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'gmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yahoo.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'hotmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'outlook.com'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strrchr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"@"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$free_providers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;invalidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Please use your business email address.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This validation runs during CF7's submission process. If it fails, CF7 marks the submission as invalid and does not proceed to the mail-sent stage—which means the connector plugin never fires the API request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 2: Schema-Woven Validation (Modern Method)
&lt;/h3&gt;

&lt;p&gt;CF7 5.9 and later use Schema-Woven Validation (SWV), a rule-based system that provides both client-side and server-side validation from a single schema . The &lt;code&gt;wpcf7_swv_create_schema&lt;/code&gt; action lets you add custom rules.&lt;/p&gt;

&lt;p&gt;The advantage of SWV is that validation rules are defined once and enforced on both the frontend and the backend. Client-side validation gives instant feedback. Server-side validation prevents bypass via direct POST requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wpcf7_swv_create_schema'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$contact_form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$rule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wpcf7_swv_create_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'field'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'company-name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Company name is required for business inquiries.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nv"&gt;$schema&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For conditional rules, SWV supports &lt;code&gt;all&lt;/code&gt; and &lt;code&gt;any&lt;/code&gt; composite rules. An &lt;code&gt;all&lt;/code&gt; rule requires every sub-rule to pass. An &lt;code&gt;any&lt;/code&gt; rule requires at least one . This is useful for validation that depends on multiple field values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating Against the API Before Submitting
&lt;/h2&gt;

&lt;p&gt;Sometimes the validation rule itself lives in the API. For example, you want to check if a phone number is already registered in your CRM before creating a new lead.&lt;/p&gt;

&lt;p&gt;You can make a lightweight validation request to the API inside your validation filter. The filter runs synchronously, so the user waits for the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wpcf7_validate_tel*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'check_phone_against_api'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;check_phone_against_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;'your-phone'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'your-phone'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;sanitize_text_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'your-phone'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_remote_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.example.com/check-phone?number='&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'timeout'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'headers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer YOUR_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;is_wp_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;wp_remote_retrieve_body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'exists'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;invalidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'This phone number is already registered.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; This adds latency to form submission. If the API is slow or unreachable, the form submission may time out. Use short timeouts and consider caching results where possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional Validation: Only Validate When Necessary
&lt;/h2&gt;

&lt;p&gt;Not every field needs validation on every submission. A budget field might only be required when the inquiry type is "Sales." A company name might only be required for business accounts.&lt;/p&gt;

&lt;p&gt;CF7's SWV supports conditional logic through composite rules . But for simpler cases, the legacy filter approach with conditional checks works reliably.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wpcf7_validate_text'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'conditionally_require_company'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;conditionally_require_company&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;'company-name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$inquiry_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'inquiry-type'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'inquiry-type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'company-name'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'company-name'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$inquiry_type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'Business'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$company&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;invalidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Company name is required for business inquiries.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern is more flexible than SWV for complex conditions that reference multiple fields or external data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Critical Timing Question: Before or After the API Call?
&lt;/h2&gt;

&lt;p&gt;The connector plugin fires the API request at a specific point in CF7's submission lifecycle. If your validation filter runs &lt;strong&gt;before&lt;/strong&gt; that point, a failed validation prevents the API call. If it runs &lt;strong&gt;after&lt;/strong&gt;, the API call fires regardless.&lt;/p&gt;

&lt;p&gt;CF7's validation filters (&lt;code&gt;wpcf7_validate_*&lt;/code&gt;) run during the validation phase, before the &lt;code&gt;wpcf7_before_send_mail&lt;/code&gt; action. The "Contact Form to Any API" plugin hooks into the submission process at a later stage. This means custom validation filters will block the API call if they fail.&lt;/p&gt;

&lt;p&gt;If you need to validate &lt;strong&gt;after&lt;/strong&gt; the API responds (for example, checking if the API accepted the data), you are working with a different problem: surfacing the API's response to the user. That requires a separate solution involving the &lt;code&gt;wpcf7mailsent&lt;/code&gt; event or custom response handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Checklist for Validation Issues
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Reveals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Submit a test form with invalid data&lt;/td&gt;
&lt;td&gt;Does CF7 show your custom error message?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Check if the API request fired&lt;/td&gt;
&lt;td&gt;If validation failed, the API call should not fire&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Verify the hook name&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;wpcf7_validate_text*&lt;/code&gt; for required, &lt;code&gt;wpcf7_validate_text&lt;/code&gt; for optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Check field name match&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;$tag-&amp;gt;name&lt;/code&gt; must match your CF7 field name exactly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Test with valid data&lt;/td&gt;
&lt;td&gt;Does the form submit and the API call fire?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Check for plugin conflicts&lt;/td&gt;
&lt;td&gt;Other CF7 extensions may alter validation behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a deeper breakdown of what happens when validation fails and no API request appears in the logs, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/" rel="noopener noreferrer"&gt;Contact Form 7 data not reaching the API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CF7's built-in validation does not know your API's rules.&lt;/strong&gt; Required, email format, and file size are not enough. Custom validation filters bridge the gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validation must run before the API call fires.&lt;/strong&gt; CF7's &lt;code&gt;wpcf7_validate_*&lt;/code&gt; filters run during the validation phase, blocking submission if they fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SWV is the modern approach.&lt;/strong&gt; Schema-Woven Validation provides client-side and server-side enforcement from a single rule definition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conditional validation prevents unnecessary friction.&lt;/strong&gt; Only require fields when the form data actually demands them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API-side validation adds latency.&lt;/strong&gt; If you check against the API inside a validation filter, use short timeouts and cache results where possible.&lt;/p&gt;

&lt;p&gt;With custom validation filters that run before the connector plugin fires, your form rejects invalid data at the source. The user gets a clear error message. The API never sees the bad request. And your CRM stays clean.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>api</category>
      <category>apiintegration</category>
    </item>
    <item>
      <title>CF7 to Salesforce Custom Object Not Created: A Complete Troubleshooting Guide</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Fri, 30 Oct 2026 10:19:29 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-salesforce-custom-object-not-created-a-complete-troubleshooting-guide-4hl2</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-salesforce-custom-object-not-created-a-complete-troubleshooting-guide-4hl2</guid>
      <description>&lt;p&gt;You have configured the "Contact Form to Any API" plugin to send Contact Form 7 submissions to a custom object in Salesforce. You have set up OAuth, entered your credentials, and mapped the fields. And yet, when a user submits the form, no record appears in your custom object.&lt;/p&gt;

&lt;p&gt;This is a common and frustrating problem. Writing to custom objects is fundamentally different from writing to standard objects like Leads. The error messages are often vague, and the root cause is usually one of four specific configuration mistakes.&lt;/p&gt;

&lt;p&gt;This guide walks through the exact reasons custom object creation fails and how to fix each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Custom Objects Are Different from Standard Objects
&lt;/h2&gt;

&lt;p&gt;Salesforce standard objects like &lt;code&gt;Lead&lt;/code&gt; or &lt;code&gt;Contact&lt;/code&gt; have well-documented, predictable API names. Custom objects do not share this predictability.&lt;/p&gt;

&lt;p&gt;Every custom object and every custom field in Salesforce has an &lt;strong&gt;API name&lt;/strong&gt; that ends with &lt;code&gt;__c&lt;/code&gt; (two underscores followed by the letter c). This suffix is not optional. If you omit it, Salesforce rejects the request with an &lt;code&gt;INVALID_FIELD&lt;/code&gt; error, even though the field exists in the UI.&lt;/p&gt;

&lt;p&gt;This is the single most common reason custom object creation fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #1: The Endpoint URL Is Missing the &lt;code&gt;__c&lt;/code&gt; Suffix
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Salesforce returns a 404 Not Found error, or the request succeeds but creates a record in the wrong place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; The endpoint URL for a custom object must include the &lt;code&gt;__c&lt;/code&gt; suffix. If your custom object is named &lt;code&gt;Project_Request&lt;/code&gt;, the API endpoint must be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/services/data/v63.0/sobjects/Project_Request__c/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you omit the &lt;code&gt;__c&lt;/code&gt;, Salesforce looks for a standard object named &lt;code&gt;Project_Request&lt;/code&gt;, which does not exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the plugin's API Endpoint URL field, verify the URL ends with &lt;code&gt;__c/&lt;/code&gt;. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://yourdomain.my.salesforce.com/services/data/v63.0/sobjects/Project_Request__c/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;yourdomain&lt;/code&gt; with your Salesforce instance URL and &lt;code&gt;Project_Request&lt;/code&gt; with your actual custom object name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to verify:&lt;/strong&gt; After submitting a test form, check the plugin's API Logs. A 404 error means the endpoint is wrong. A 201 Created response means the record was created successfully. For a deeper breakdown of what empty logs mean, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/" rel="noopener noreferrer"&gt;Contact Form 7 data not reaching the API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #2: The Field Names Are Missing the &lt;code&gt;__c&lt;/code&gt; Suffix
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Salesforce returns an &lt;code&gt;INVALID_FIELD&lt;/code&gt; error with a message like: "No such column 'Project_Type' on sobject of type Project_Request__c".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Just like the object name, every custom field's API name ends with &lt;code&gt;__c&lt;/code&gt;. If your custom field is labeled "Project Type" in the Salesforce UI, its API name might be &lt;code&gt;Project_Type__c&lt;/code&gt;. In your JSON payload, you must use the API name, not the label.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; In Salesforce, navigate to Setup → Object Manager → Your Custom Object → Fields &amp;amp; Relationships. For each field you want to map, copy the &lt;strong&gt;API Name&lt;/strong&gt; exactly as shown, including &lt;code&gt;__c&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; In the plugin's JSON payload, use the API name, not the label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Project_Type__c"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[project-type]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Budget__c"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[budget-range]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Verify the field names match exactly. One missing underscore means a failed request.&lt;/p&gt;

&lt;p&gt;For a deeper dive into JSON structure mapping and field naming conventions, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #3: The User Lacks Permission to Create Records on the Custom Object
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Salesforce returns a 403 Forbidden error, or an &lt;code&gt;INSUFFICIENT_ACCESS&lt;/code&gt; error. The API call authenticated successfully but was rejected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Creating a custom object does not automatically grant permission to write to it. As a Salesforce administrator, you can see the object in Setup. But API access requires explicit permission on the object and on every custom field you want to populate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; In Salesforce, navigate to Setup → Users → Profiles (or Permission Sets).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Find the profile or permission set associated with the user whose credentials the plugin uses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Grant the following permissions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Object Permissions:&lt;/strong&gt; Create, Read, Edit on the custom object&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field-Level Security:&lt;/strong&gt; Edit access on every custom field you are mapping&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Save the permissions. Wait a few minutes for Salesforce to propagate the changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to verify:&lt;/strong&gt; After updating permissions, submit a test form. A 201 response means the record was created.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #4: The OAuth Token Does Not Have the Correct Scope
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Salesforce returns a 401 Unauthorized error, or the token works for standard objects but fails for custom objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Salesforce OAuth tokens are scoped. The &lt;code&gt;api&lt;/code&gt; scope grants access to standard objects, but custom objects may require additional permissions or a different scope configuration.&lt;/p&gt;

&lt;p&gt;The "Contact Form to Any API" plugin's PRO version with the OAuth Add-on handles Salesforce's OAuth 2.0 flow, including automatic token refresh. If you are using the free version without the OAuth Add-on, token management may be incomplete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Verify you are using the PRO version with the OAuth Add-on. The free version does not support automatic token renewal, which means expired tokens will cause failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; In the plugin's Auth Master settings, confirm the OAuth scopes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;api&lt;/code&gt; (Access and manage your data)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;refresh_token&lt;/code&gt; and &lt;code&gt;offline_access&lt;/code&gt; (for automatic token refresh)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Re-authorize the connection. In the plugin, click Authorize again to generate a fresh token with the correct scopes.&lt;/p&gt;

&lt;p&gt;For a comprehensive breakdown of authentication errors across different APIs, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Checklist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Reveals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Check plugin API Logs&lt;/td&gt;
&lt;td&gt;Was a request sent? What response code?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Verify endpoint URL ends with &lt;code&gt;__c/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Custom object endpoint format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Verify field API names end with &lt;code&gt;__c&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Field naming convention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Check user permissions on the object&lt;/td&gt;
&lt;td&gt;Object-level create/edit access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Check field-level security&lt;/td&gt;
&lt;td&gt;Edit access on each mapped field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Verify OAuth token scopes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;api&lt;/code&gt;, &lt;code&gt;refresh_token&lt;/code&gt;, &lt;code&gt;offline_access&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Beyond Custom Objects: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your custom object integration works, you might want to send the same submission to other Salesforce objects or external tools.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can create a custom object record, a standard Lead, and send an SMS via Twilio, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Every custom object and custom field API name ends with &lt;code&gt;__c&lt;/code&gt;.&lt;/strong&gt; Omitting this suffix is the most common cause of &lt;code&gt;INVALID_FIELD&lt;/code&gt; and 404 errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating the object does not grant write permission.&lt;/strong&gt; You must explicitly grant Create/Edit permissions on the object and Edit access on every custom field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The endpoint URL must target the custom object by its API name.&lt;/strong&gt; &lt;code&gt;/sobjects/Your_Object__c/&lt;/code&gt; is the correct format.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OAuth scopes must include &lt;code&gt;api&lt;/code&gt; and refresh capabilities.&lt;/strong&gt; Without proper scopes, the token may work for standard objects but fail for custom ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the PRO version with the OAuth Add-on for Salesforce.&lt;/strong&gt; The free version does not handle automatic token refresh, which is essential for long-term reliability.&lt;/p&gt;

&lt;p&gt;With the correct &lt;code&gt;__c&lt;/code&gt; suffixes, proper permissions, and valid OAuth scopes, your CF7-to-Salesforce custom object integration will create records reliably.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>salesforce</category>
      <category>customobjects</category>
    </item>
    <item>
      <title>CF7 API Response Not Showing to User: Why It Happens and How to Fix It</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Thu, 29 Oct 2026 09:35:18 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-api-response-not-showing-to-user-why-it-happens-and-how-to-fix-it-672</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-api-response-not-showing-to-user-why-it-happens-and-how-to-fix-it-672</guid>
      <description>&lt;p&gt;You have configured the "Contact Form to Any API" plugin to send form submissions to an external service. The API returns a meaningful response a success confirmation, a validation error, a duplicate email warning, or a custom message. And yet, the user sees nothing. The form submits, the default CF7 success message appears, and the API's response is buried in the plugin's admin logs.&lt;/p&gt;

&lt;p&gt;This is not a bug. It is a documented limitation of the plugin's architecture. But it creates a frustrating user experience, especially when the API rejects the submission for a reason the user needs to know about.&lt;/p&gt;

&lt;p&gt;This guide explains why API responses are hidden from users and walks through the verified workarounds to surface them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Plugin Hides API Responses from Users
&lt;/h2&gt;

&lt;p&gt;The "Contact Form to Any API" plugin hooks into Contact Form 7's submission process at the &lt;code&gt;wpcf7_mail_sent&lt;/code&gt; action. This means the API request fires &lt;strong&gt;after&lt;/strong&gt; CF7 has already decided the submission was successful and displayed its default success message.&lt;/p&gt;

&lt;p&gt;From the plugin's perspective, the API call is a side effect of a successful form submission. The plugin logs the request and response for administrative review, but it does not inject that response into CF7's frontend message system. The plugin's official documentation describes the Logs feature as showing "your API response" for admin review, not for user display.&lt;/p&gt;

&lt;p&gt;The developer confirmed this limitation directly: the plugin was "created after form successfully submitted" and "at the moment it is not possible to manage API side errors to display on cf7 forms".&lt;/p&gt;

&lt;p&gt;If no request appears in the logs at all, the problem may be earlier in the pipeline. For a deeper breakdown of that scenario, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/" rel="noopener noreferrer"&gt;Contact Form 7 data not reaching the API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Consequence: Users See Success When the API Failed
&lt;/h2&gt;

&lt;p&gt;Because the API call happens after CF7's success determination, a user can see "Thank you for your message" even when the API rejected the submission entirely.&lt;/p&gt;

&lt;p&gt;This is a serious problem for workflows where the API response contains information the user needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A duplicate email warning that should prompt the user to log in instead&lt;/li&gt;
&lt;li&gt;A validation error about a phone number format&lt;/li&gt;
&lt;li&gt;A capacity limit message for an event registration&lt;/li&gt;
&lt;li&gt;A custom confirmation number or reference ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The user believes their submission succeeded. The API knows it failed. And the only record of the failure is in an admin log the user cannot see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workaround 1: Hook into &lt;code&gt;wpcf7_before_send_mail&lt;/code&gt; and Inject a Custom Message
&lt;/h2&gt;

&lt;p&gt;The most direct workaround, provided by the plugin's developer in a support thread, involves changing the hook timing and using CF7's &lt;code&gt;wpcf7_display_message&lt;/code&gt; filter to replace the success message when the API returns an error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Change the Hook from &lt;code&gt;wpcf7_mail_sent&lt;/code&gt; to &lt;code&gt;wpcf7_before_send_mail&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In the plugin file &lt;code&gt;contact-form-to-any-api/includes/class-cf7-to-any-api.php&lt;/code&gt;, line 169, replace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wpcf7_mail_sent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$plugin_admin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'cf7_to_any_api_send_data_to_api'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wpcf7_before_send_mail'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$plugin_admin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'cf7_to_any_api_send_data_to_api'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the API call fire &lt;strong&gt;before&lt;/strong&gt; CF7 determines success, giving you a chance to intercept the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Add a Filter to Check the API Response and Change the Message
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;class-cf7-to-any-api-admin.php&lt;/code&gt;, after line 634, add logic that checks the API response and hooks into &lt;code&gt;wpcf7_display_message&lt;/code&gt; when an error condition is met:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$api_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"400"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$api_response&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;
        &lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wpcf7_display_message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'change_submission_msg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;change_submission_msg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mail_sent_ok'&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
                &lt;span class="nv"&gt;$message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'We are unable to proceed with your request. The email address you entered is already on our server.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important caveat:&lt;/strong&gt; The developer noted that the plugin has "no plan to include such validation in our plugin" at the time of that thread. This workaround modifies core plugin files. Plugin updates will overwrite these changes. You must reapply the modification after every update, or fork the plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workaround 2: Use the &lt;code&gt;wpcf7mailsent&lt;/code&gt; JavaScript Event and &lt;code&gt;add_result_props&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A cleaner approach, documented on Stack Overflow, uses CF7's &lt;code&gt;add_result_props&lt;/code&gt; method to pass the API response to a JavaScript event listener.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: In Your &lt;code&gt;functions.php&lt;/code&gt;, Hook into &lt;code&gt;wpcf7_before_send_mail&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wpcf7_before_send_mail'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'send_api_and_capture_response'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;send_api_and_capture_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$contact_form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$submission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WPCF7_Submission&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get_instance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$submission&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Your API call logic here&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_remote_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_remote_retrieve_body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Pass the API response to the frontend via the wpcf7mailsent event&lt;/span&gt;
    &lt;span class="nv"&gt;$submission&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add_result_props&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'api_response'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;
    &lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Listen for the Event in JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wpcf7mailsent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Display the API response to the user&lt;/span&gt;
        &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Or inject it into a custom DOM element&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.your-message-container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method is &lt;strong&gt;update-safe&lt;/strong&gt; because it uses hooks and does not modify plugin core files. It requires custom PHP and JavaScript but avoids the maintenance burden of patching the plugin directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workaround 3: Use a Dedicated Plugin with Built-in Response Display
&lt;/h2&gt;

&lt;p&gt;A different plugin, "Contact Form 7 to API," explicitly advertises the feature you need: "Option to show the API response body as the form message". Its documentation states: "Turn on 'Show the API response body as the form message' to show a JSON message field, or a shortened copy of any other body".&lt;/p&gt;

&lt;p&gt;If displaying the API response to the user is a hard requirement, switching to a plugin with this feature built-in may be the most reliable path. The trade-off is migrating your existing integrations and field mappings to a different plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workaround 4: Use the Plugin's Logs for Admin-Only Visibility
&lt;/h2&gt;

&lt;p&gt;If the API response does not need to be shown to the user but you need to monitor it, the plugin's Logs tab already provides this. The documentation confirms: "You can see your API response too".&lt;/p&gt;

&lt;p&gt;This is the default behavior. It is useful for debugging and auditing but does not solve the user-facing problem. For a comprehensive breakdown of authentication errors and how they appear in logs, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Checklist for API Response Issues
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Reveals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Check the plugin's Logs tab&lt;/td&gt;
&lt;td&gt;The exact API response for each submission&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Determine if the API call fires before or after CF7 success&lt;/td&gt;
&lt;td&gt;Check the hook in &lt;code&gt;class-cf7-to-any-api.php&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Check if the API returns errors but CF7 still shows success&lt;/td&gt;
&lt;td&gt;Compare log entries to user reports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Decide if the response needs user visibility&lt;/td&gt;
&lt;td&gt;If yes, choose a workaround above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Test the workaround with a forced API error&lt;/td&gt;
&lt;td&gt;Verify the custom message appears&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Beyond the Basics: What Nobody Tells You
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The hook order matters more than the payload.&lt;/strong&gt; Most debugging guides focus on JSON structure and authentication. But if the API call fires after CF7 has already committed to a success message, no amount of payload debugging will show the error to the user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;add_result_props&lt;/code&gt; is the most sustainable approach.&lt;/strong&gt; It survives plugin updates and does not require modifying core files. The trade-off is that you must write the PHP and JavaScript yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A different plugin solves this natively.&lt;/strong&gt; If user-facing API feedback is a hard requirement for your workflow, evaluate "Contact Form 7 to API" which has a dedicated option for showing the API response body as the form message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The logs are your first diagnostic tool.&lt;/strong&gt; Before attempting any workaround, check the plugin's Logs tab. If the API response is there but the user does not see it, you have confirmed the visibility problem. If the logs are empty, the problem is earlier in the pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The plugin does not display API responses to users by default.&lt;/strong&gt; The developer confirmed this limitation in a support thread: API errors are not shown as frontend validation messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The API call fires after CF7's success determination.&lt;/strong&gt; This means users can see a success message even when the API rejected the submission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The developer-provided workaround involves modifying plugin core files.&lt;/strong&gt; This is update-unsafe. You must reapply the change after every plugin update, or use the &lt;code&gt;add_result_props&lt;/code&gt; method with JavaScript instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A different plugin advertises built-in API response display.&lt;/strong&gt; If this feature is critical, evaluate "Contact Form 7 to API" which has a dedicated option for showing the API response body as the form message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For admin-only visibility, the plugin's Logs tab already works.&lt;/strong&gt; Use it for debugging and auditing, but do not expect it to inform the user.&lt;/p&gt;

&lt;p&gt;With the right workaround, you can bridge the gap between the plugin's admin-focused logging and the user's need for feedback. The &lt;code&gt;add_result_props&lt;/code&gt; method is the most sustainable approach because it survives plugin updates.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>apiintegration</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CF7 to WhatsApp Business API via Twilio: The Complete Implementation Guide</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Wed, 28 Oct 2026 10:11:34 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-whatsapp-business-api-via-twilio-the-complete-implementation-guide-52l1</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-whatsapp-business-api-via-twilio-the-complete-implementation-guide-52l1</guid>
      <description>&lt;p&gt;You want form submissions from your Contact Form 7 to arrive as WhatsApp messages. Not emails that sit unread. Not SMS that costs per message. WhatsApp messages that your team actually sees and responds to.&lt;/p&gt;

&lt;p&gt;The "Contact Form to Any API" plugin can do this. But the setup has specific requirements that trip up most people: template approval, Base64 authentication, and Twilio's strict payload format. This guide walks through the exact configuration, the errors you will hit, and how to fix them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: Why This Is Not Just Another API Call
&lt;/h2&gt;

&lt;p&gt;WhatsApp Business API is fundamentally different from email or SMS APIs. It enforces strict rules on who can message whom, and when.&lt;/p&gt;

&lt;p&gt;The data flow looks simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CF7 Form → Contact Form to Any API → Twilio Messages API → WhatsApp Message Delivered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the constraints are not simple. WhatsApp divides messages into two types: &lt;strong&gt;Session messages&lt;/strong&gt; and &lt;strong&gt;Template messages&lt;/strong&gt;. A session starts when a user messages your business first. Within that 24-hour window, you can send free-form replies. Outside that window, you can only send pre-approved templates. Since a CF7 form submission is business-initiated, you must use a template. There is no workaround.&lt;/p&gt;

&lt;p&gt;This is the first thing most guides gloss over. If you skip template creation, your messages will fail silently or return error 63016.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites: What You Need Before Touching the Plugin
&lt;/h2&gt;

&lt;p&gt;Three things must be in place before you configure a single field in WordPress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Twilio account with WhatsApp enabled.&lt;/strong&gt; You need your Account SID and Auth Token. For production, you also need a registered WhatsApp sender. The Twilio Sandbox is for testing only and expires after three days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An approved WhatsApp message template.&lt;/strong&gt; This is non-negotiable. You create it in Twilio's Content Template Builder, submit it for WhatsApp review, and wait for approval. Machine review typically takes minutes. Manual review can take up to 48 hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A clear understanding of ContentSid and ContentVariables.&lt;/strong&gt; Twilio's modern WhatsApp API uses these parameters, not a raw &lt;code&gt;Body&lt;/code&gt; parameter. Using &lt;code&gt;Body&lt;/code&gt; outside a conversation window will fail with error 63016.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Template That Will Actually Get Approved
&lt;/h2&gt;

&lt;p&gt;WhatsApp rejects templates for reasons that are not always obvious. Understanding the rejection criteria saves days of back-and-forth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The most common rejection reasons&lt;/strong&gt; according to Twilio's documentation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rejection Reason&lt;/th&gt;
&lt;th&gt;What It Means&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Variable at the start or end&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;{{1}}, thank you&lt;/code&gt; or &lt;code&gt;Hello {{1}}&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Add text before or after the variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adjacent variables&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{1}}{{2}}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Insert a word between them or combine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-sequential variables&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{1}}, {{2}}, {{4}}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use sequential numbering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Too vague&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Hi {{1}}, thanks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add context about why the message exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Newlines or tabs&lt;/td&gt;
&lt;td&gt;Extra whitespace&lt;/td&gt;
&lt;td&gt;Remove them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate content&lt;/td&gt;
&lt;td&gt;Same template, different name&lt;/td&gt;
&lt;td&gt;Modify both name and content&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A template that will be approved&lt;/strong&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello {{1}}, you have a new inquiry from {{2}}. Their message: {{3}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This has context (new inquiry), sequential variables, and text before and after each placeholder. It will pass machine review quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copy the Template SID&lt;/strong&gt; once approved. It starts with &lt;code&gt;HX&lt;/code&gt;. You will need it for the plugin payload.&lt;/p&gt;

&lt;p&gt;For a deeper dive into JSON structure mapping and variable handling, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Authenticate with Base64 (The Step Everyone Gets Wrong)
&lt;/h2&gt;

&lt;p&gt;Twilio uses HTTP Basic Authentication. The plugin's header field is a static textarea. PHP functions like &lt;code&gt;base64_encode()&lt;/code&gt; do not execute here. You must encode the credentials manually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Twilio's official recommendation&lt;/strong&gt; is to use API Keys, not your Account SID and Auth Token, for production. However, the plugin's official Twilio guide uses Account SID and Auth Token for simplicity. For a production integration handling real customer data, create an API Key in Twilio Console and use that instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The encoding process:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Combine your credentials: &lt;code&gt;ACCOUNT_SID:AUTH_TOKEN&lt;/code&gt; (or &lt;code&gt;API_KEY_SID:API_KEY_SECRET&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Base64-encode the combined string&lt;/li&gt;
&lt;li&gt;In the plugin's Request Headers field, add:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: Basic YOUR_BASE64_ENCODED_STRING
Content-Type: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you get a 401 error, the encoding is wrong. Verify there are no spaces before or after the encoded string.&lt;/p&gt;

&lt;p&gt;For a comprehensive breakdown of authentication options, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Build the Payload That Twilio Actually Expects
&lt;/h2&gt;

&lt;p&gt;The plugin's official Twilio WhatsApp documentation provides this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"To"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"whatsapp:[phone-number]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"From"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"whatsapp:YOUR_TWILIO_WHATSAPP_NUMBER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ContentSid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HX_YOUR_TEMPLATE_SID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ContentVariables"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-email]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-message]"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical details that cause failures:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;To&lt;/code&gt; must include &lt;code&gt;whatsapp:&lt;/code&gt;&lt;/strong&gt; before the phone number. Without it, Twilio returns error 63007 (channel not found).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;From&lt;/code&gt; must be your registered WhatsApp sender&lt;/strong&gt;, not a regular SMS number. For Sandbox testing, use &lt;code&gt;whatsapp:+14155238886&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ContentVariables&lt;/code&gt; keys must match your template's variable count.&lt;/strong&gt; If your template has three variables, your payload must have exactly three keys. A mismatch returns error 132000.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The number of variables in the template determines the number of ContentVariables entries.&lt;/strong&gt; You cannot add extra variables that do not exist in the approved template.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Test with the Sandbox Before Going Live
&lt;/h2&gt;

&lt;p&gt;The Twilio Sandbox is the only way to test without a production WhatsApp sender. But it has limitations that confuse people.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sandbox limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recipients must join your sandbox by sending &lt;code&gt;join &amp;lt;code&amp;gt;&lt;/code&gt; to the sandbox number&lt;/li&gt;
&lt;li&gt;Sessions expire after three days&lt;/li&gt;
&lt;li&gt;Only pre-approved templates work (you cannot use custom templates)&lt;/li&gt;
&lt;li&gt;One message every three seconds rate limit&lt;/li&gt;
&lt;li&gt;Not available in the new Twilio Console; use the legacy Console&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;To test:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Activate the Sandbox in the legacy Twilio Console&lt;/li&gt;
&lt;li&gt;Have your phone join the sandbox&lt;/li&gt;
&lt;li&gt;Configure the plugin with &lt;code&gt;whatsapp:+14155238886&lt;/code&gt; as the &lt;code&gt;From&lt;/code&gt; value&lt;/li&gt;
&lt;li&gt;Use one of the Sandbox's pre-approved templates&lt;/li&gt;
&lt;li&gt;Submit a test form and verify the message arrives&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the sandbox works but production fails, the issue is your production sender's status or template approval. Check that your sender's status is &lt;code&gt;ONLINE&lt;/code&gt; in Twilio Console.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Errors You Will Hit and Exactly How to Fix Them
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Error 63007: Channel Not Found
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it means:&lt;/strong&gt; The &lt;code&gt;From&lt;/code&gt; address is not formatted correctly or is not associated with a WhatsApp sender.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Verify the &lt;code&gt;From&lt;/code&gt; value uses the exact format &lt;code&gt;whatsapp:+[number]&lt;/code&gt;. If using Sandbox, confirm the sandbox is activated in the same account. If using a live sender, check that the sender status is &lt;code&gt;ONLINE&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error 63016: Outside the 24-Hour Window
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it means:&lt;/strong&gt; You are trying to send a free-form message outside the 24-hour customer service window, or you are using the &lt;code&gt;Body&lt;/code&gt; parameter instead of &lt;code&gt;ContentSid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use &lt;code&gt;ContentSid&lt;/code&gt; and &lt;code&gt;ContentVariables&lt;/code&gt;. Never use &lt;code&gt;Body&lt;/code&gt; for business-initiated messages. This is the single most common production failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error 63040: Template Rejected
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it means:&lt;/strong&gt; The template was rejected by WhatsApp, or your payload matches a rejected template.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Check the rejection reason in Twilio Console. Common causes: vague content, non-sequential variables, placeholders at the start or end. Submit a new template with a different name. WhatsApp prevents reuse of a rejected name for 30 days.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error 132000: Template Param Count Mismatch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it means:&lt;/strong&gt; The number of variables in &lt;code&gt;ContentVariables&lt;/code&gt; does not match the template's variable count.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Count the &lt;code&gt;{{1}}&lt;/code&gt;, &lt;code&gt;{{2}}&lt;/code&gt;, etc. in your approved template. Your &lt;code&gt;ContentVariables&lt;/code&gt; object must have exactly that many keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Checklist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Reveals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Check plugin API Logs&lt;/td&gt;
&lt;td&gt;Was a request sent? What response code?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Check Twilio Console &amp;gt; Monitor &amp;gt; Logs&lt;/td&gt;
&lt;td&gt;Detailed error code and message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Verify template status&lt;/td&gt;
&lt;td&gt;Is it Approved? Pending? Rejected?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;From&lt;/code&gt; format&lt;/td&gt;
&lt;td&gt;Does it start with &lt;code&gt;whatsapp:&lt;/code&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;ContentSid&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Does it match your approved template?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Count ContentVariables&lt;/td&gt;
&lt;td&gt;Does it match the template's variable count?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Verify Base64 encoding&lt;/td&gt;
&lt;td&gt;Is the Authorization header correctly formatted?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Beyond the Basics: What Nobody Tells You
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp charges per template message, not per session message.&lt;/strong&gt; Two-way conversations (where the user replies) are cheaper. Design your template to encourage a reply. A template like "Reply YES to confirm" opens a session and reduces costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Template approval is not permanent.&lt;/strong&gt; Templates can be paused or disabled if users repeatedly block or report your messages. Monitor template quality in Twilio Console. If a template is paused, messages using it will fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sandbox is not a production environment.&lt;/strong&gt; It expires. It has rate limits. It displays the Twilio logo. Use it to validate your payload and authentication. Then move to a production sender before going live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond WhatsApp: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your WhatsApp integration works, you might want to send the same form submission to other tools: a CRM, an email marketing platform, or a help desk system.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can send a WhatsApp message via Twilio, create a CRM contact, and subscribe the user to Mailchimp, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp Business API requires pre-approved templates.&lt;/strong&gt; There is no free-form workaround for business-initiated messages. Create and approve a template before configuring the plugin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ContentSid&lt;/code&gt; and &lt;code&gt;ContentVariables&lt;/code&gt; are mandatory.&lt;/strong&gt; The &lt;code&gt;Body&lt;/code&gt; parameter will fail with error 63016 outside a conversation window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Base64 encoding must be done manually.&lt;/strong&gt; The plugin's header field does not execute PHP. Encode &lt;code&gt;ACCOUNT_SID:AUTH_TOKEN&lt;/code&gt; externally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;From&lt;/code&gt; value must use the &lt;code&gt;whatsapp:&lt;/code&gt; prefix.&lt;/strong&gt; Error 63007 is the result of forgetting this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test with Sandbox, but do not ship with it.&lt;/strong&gt; Sandbox sessions expire and templates are limited. Move to a production sender before going live.&lt;/p&gt;

&lt;p&gt;With an approved template, correctly encoded credentials, and a payload that matches Twilio's modern Content API, your CF7 form submissions will arrive as WhatsApp messages on your team's phones. No missed inquiries. No email delays.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>whatsapp</category>
      <category>twilio</category>
      <category>apiintegration</category>
    </item>
    <item>
      <title>CF7 GDPR Compliant API Integration: A Complete Implementation Guide</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Tue, 27 Oct 2026 09:51:58 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-gdpr-compliant-api-integration-a-complete-implementation-guide-19hp</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-gdpr-compliant-api-integration-a-complete-implementation-guide-19hp</guid>
      <description>&lt;p&gt;You have connected Contact Form 7 to an external API. Leads flow into your CRM, contacts sync to your marketing platform, and data moves automatically. Everything works.&lt;/p&gt;

&lt;p&gt;But if you collect data from visitors in the EU, you have a legal obligation to ensure that data transfer is GDPR compliant. The plugin that sends the data, the API that receives it, and the way you collect consent all matter.&lt;/p&gt;

&lt;p&gt;This guide walks through the practical steps to make your CF7 API integration GDPR compliant, based on the plugin's official compliance documentation and standard GDPR requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why GDPR Compliance Matters for API Integrations
&lt;/h2&gt;

&lt;p&gt;The GDPR applies whenever you process personal data of individuals in the EU. A contact form submission contains personal data: name, email, phone, message. When you send that data to an external API, you are transferring it to a third party.&lt;/p&gt;

&lt;p&gt;Article 28 of GDPR requires a contract between you (the data controller) and the API provider (the data processor). Article 32 requires appropriate technical measures to protect the data during transfer. And the data minimization principle means you should only collect what you actually need.&lt;/p&gt;

&lt;p&gt;If you skip these steps, you are exposed to regulatory fines, data breach liability, and reputational damage. "I didn't know the API provider wasn't compliant" does not hold up as a defense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Get Explicit Consent (If Required)
&lt;/h2&gt;

&lt;p&gt;Not every form submission requires consent. A contact form processed under a pre-contractual basis (Article 6(1)(b)) may not need a checkbox. But if you are sending data to a marketing platform, a CRM for prospecting, or any system that uses the data for purposes beyond fulfilling the immediate request, you need explicit consent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The consent text must be specific.&lt;/strong&gt; A non-compliant example: "I agree to the terms."&lt;/p&gt;

&lt;p&gt;A compliant version: "I agree to receive marketing emails from [Your Company] and understand my data will be shared with [third-party tool]. I can unsubscribe at any time."&lt;/p&gt;

&lt;p&gt;The checkbox must be &lt;strong&gt;unchecked by default&lt;/strong&gt;. The user must actively opt in. Pre-ticked boxes are not valid consent under GDPR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Apply Data Minimization
&lt;/h2&gt;

&lt;p&gt;This is where API integrations often fail. It is tempting to map every form field to every destination. The GDPR expects you to collect and transmit only what is necessary for the stated purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ask the hard question:&lt;/strong&gt; Does your CRM actually need the phone number if you are only sending emails? Does your marketing tool need the full message body if it only uses name and email?&lt;/p&gt;

&lt;p&gt;With the "Contact Form to Any API" plugin, you control field mapping individually. There are no automatic data dumps where everything gets sent whether it is needed or not. For each API endpoint, review the field mappings and strip out anything that is not essential for that specific integration.&lt;/p&gt;

&lt;p&gt;If you send the same form to multiple APIs (a CRM, a marketing tool, and a spreadsheet), review each destination separately. Each endpoint might receive data it does not actually use.&lt;/p&gt;

&lt;p&gt;For a deeper dive into JSON structure mapping and selective field inclusion, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Ensure Encrypted Transmission
&lt;/h2&gt;

&lt;p&gt;GDPR Article 32 requires appropriate technical measures to protect personal data. For API integrations, this means two things: encryption and authentication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All data sent to APIs must use HTTPS/TLS encryption.&lt;/strong&gt; TLS encrypts data while it travels between your server and the API endpoint, preventing interception. If an API endpoint only accepts HTTP, that is a red flag. Do not use it for personal data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Use Proper Authentication
&lt;/h2&gt;

&lt;p&gt;Authentication verifies that your requests are legitimate and prevents unauthorized access. The plugin supports Basic Auth, Bearer Token, and OAuth 2.0 natively.&lt;/p&gt;

&lt;p&gt;For GDPR-sensitive integrations, prefer Bearer Token or OAuth 2.0 over Basic Auth. Basic Auth sends credentials with every request, and while TLS encrypts the transmission, the credentials are still less protected than token-based methods.&lt;/p&gt;

&lt;p&gt;For a comprehensive breakdown of authentication options and their security implications, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Verify Your API Provider's GDPR Compliance
&lt;/h2&gt;

&lt;p&gt;You are responsible for the data you send to third parties. Before connecting to any external API, verify the provider's compliance posture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions to ask:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the provider publish a GDPR compliance statement?&lt;/li&gt;
&lt;li&gt;Where are their data servers physically located?&lt;/li&gt;
&lt;li&gt;Do they offer Data Processing Agreements (DPAs)?&lt;/li&gt;
&lt;li&gt;What security certifications do they hold (SOC 2, ISO 27001)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the provider cannot answer these questions, find a different provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Sign a Data Processing Agreement
&lt;/h2&gt;

&lt;p&gt;A DPA is a legally binding contract between you (the controller) and the API provider (the processor). Article 28 of GDPR requires this arrangement whenever a processor handles personal data on your behalf.&lt;/p&gt;

&lt;p&gt;Most major CRMs and marketing platforms offer standard DPAs. You typically sign them through the provider's settings or legal portal. If a provider does not offer a DPA, that is a serious compliance gap.&lt;/p&gt;

&lt;p&gt;The DPA should specify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What data the processor handles&lt;/li&gt;
&lt;li&gt;Why and for how long&lt;/li&gt;
&lt;li&gt;The processor's security obligations&lt;/li&gt;
&lt;li&gt;How sub-processors are managed&lt;/li&gt;
&lt;li&gt;Data transfer safeguards if data leaves the EU&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 7: Handle Cross-Border Data Transfers
&lt;/h2&gt;

&lt;p&gt;If your API provider stores or processes data outside the EU/EEA, you need additional safeguards. These include adequacy decisions (where the European Commission has deemed a country's data protection laws sufficient) or Standard Contractual Clauses (SCCs) for countries without adequacy status.&lt;/p&gt;

&lt;p&gt;The US has adequacy through the Data Privacy Framework for certified companies. If your API provider is US-based, verify that they are certified under this framework before sending EU personal data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Set Retention Periods
&lt;/h2&gt;

&lt;p&gt;GDPR's storage limitation principle means you cannot keep personal data longer than necessary. There is no universal retention period in the regulation. It depends on your purpose.&lt;/p&gt;

&lt;p&gt;Define and document how long you keep form data. If someone inquired about a product two years ago and never responded to follow-up, do you still have a legitimate reason to keep their data? If not, delete it.&lt;/p&gt;

&lt;p&gt;The plugin's API logs should also be considered. These logs may contain personal data from API requests. Review your log retention settings and ensure they align with your data retention policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The GDPR Compliance Checklist for CF7 API Integrations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;GDPR Reference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Add explicit consent checkbox (unchecked by default) for marketing/prospecting data&lt;/td&gt;
&lt;td&gt;Art. 6(1)(a), Art. 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Map only necessary fields to each API endpoint&lt;/td&gt;
&lt;td&gt;Art. 5(1)(c)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Ensure API endpoint uses HTTPS/TLS&lt;/td&gt;
&lt;td&gt;Art. 32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Use Bearer Token or OAuth, not Basic Auth, for sensitive data&lt;/td&gt;
&lt;td&gt;Art. 32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Verify API provider's GDPR statement and server locations&lt;/td&gt;
&lt;td&gt;Art. 28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Sign a Data Processing Agreement with the provider&lt;/td&gt;
&lt;td&gt;Art. 28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Verify adequacy or SCCs for cross-border transfers&lt;/td&gt;
&lt;td&gt;Art. 44-49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Set and enforce data retention periods&lt;/td&gt;
&lt;td&gt;Art. 5(1)(e)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Review API log retention settings&lt;/td&gt;
&lt;td&gt;Art. 5(1)(e)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Beyond GDPR: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your GDPR-compliant integration works, you might want to send the same submission to other tools: a CRM, an email marketing platform, or a help desk system.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can create a CRM contact, subscribe the user to Mailchimp, and send an SMS via Twilio, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GDPR compliance is not optional for EU data.&lt;/strong&gt; If you collect data from EU visitors and send it to an API, you need consent (where applicable), encryption, a DPA, and data minimization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data minimization is where most integrations fail.&lt;/strong&gt; The plugin lets you map fields individually. Use that control. Do not send everything to every endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The plugin supports GDPR-compliant authentication.&lt;/strong&gt; Bearer Token and OAuth 2.0 are available natively. Prefer these over Basic Auth for sensitive integrations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify your API provider before connecting.&lt;/strong&gt; A provider without a DPA or a clear GDPR statement is a compliance risk you do not need to take.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sign the DPA before you go live.&lt;/strong&gt; It is a legal requirement, not a formality.&lt;/p&gt;

&lt;p&gt;With explicit consent, minimal field mapping, encrypted transmission, and a signed DPA, your CF7 API integration can be both functional and compliant. The plugin provides the technical tools. The compliance work is yours to complete.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>api</category>
      <category>compliance</category>
    </item>
    <item>
      <title>CF7 to Copper CRM Not Creating Leads: A Complete Troubleshooting Guide</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Mon, 26 Oct 2026 08:57:59 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-copper-crm-not-creating-leads-a-complete-troubleshooting-guide-32h1</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-copper-crm-not-creating-leads-a-complete-troubleshooting-guide-32h1</guid>
      <description>&lt;p&gt;You have built a Contact Form 7 form. You have connected it to Copper CRM through the "Contact Form to Any API" plugin. You have entered your API key and mapped the fields. And yet, when a user submits the form, no lead appears in your Copper CRM.&lt;/p&gt;

&lt;p&gt;This is a frustratingly common problem. The good news? The issue is almost always one of four things, and each has a clear fix. This guide walks through the exact debugging process, common error patterns, and verified solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Data Flow
&lt;/h2&gt;

&lt;p&gt;When a user submits your CF7 form with Copper lead creation enabled, here is what should happen:&lt;/p&gt;

&lt;p&gt;User submits form → Plugin captures data → POST request sent to Copper API → Lead created&lt;/p&gt;

&lt;p&gt;When it breaks, the failure happens at one of the last two stages. Your first diagnostic tool is the &lt;strong&gt;plugin's API Logs page&lt;/strong&gt;. It shows whether a request was sent, the exact JSON payload, and the response from Copper. If no request appears in the logs, the problem is on the WordPress side. For a deeper breakdown of that specific scenario, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/" rel="noopener noreferrer"&gt;Contact Form 7 data not reaching the API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #1: The Endpoint URL Is Wrong
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; No lead is created. The API logs show a 404 Not Found error, or a successful request that creates a Person instead of a Lead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Copper separates People and Leads into distinct API endpoints. The plugin requires you to manually enter the endpoint URL. If you paste the People endpoint instead of the Leads endpoint, the plugin will create a Person record, not a Lead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Verify your endpoint URL in the plugin settings. For creating leads, it must be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.copper.com/developer_api/v1/leads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The People endpoint is &lt;code&gt;/people&lt;/code&gt;. The Leads endpoint is &lt;code&gt;/leads&lt;/code&gt;. These are not interchangeable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to verify:&lt;/strong&gt; After submitting a test form, check your Copper account. Navigate to the Leads section. If no new lead appears, the endpoint is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #2: The Authentication Headers Are Missing or Incorrect
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Copper returns a 401 Unauthorized or 403 Forbidden error. No lead is created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Copper requires three specific headers for API authentication. The plugin provides a static textarea for request headers. If any of these are missing or incorrectly formatted, authentication fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the plugin's Request Headers field, add all three required headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;X-PW-AccessToken: YOUR_API_KEY
X-PW-Application: developer_api
X-PW-UserEmail: your-login-email@example.com
Content-Type: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;X-PW-AccessToken:&lt;/strong&gt; Your Copper API key, generated from Profile → Email Settings &amp;amp; Templates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X-PW-Application:&lt;/strong&gt; Must be exactly &lt;code&gt;developer_api&lt;/code&gt;. This is not your application name; it is a fixed value required by Copper.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X-PW-UserEmail:&lt;/strong&gt; The email address you use to log in to Copper. This must match exactly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to verify:&lt;/strong&gt; After saving, submit a test form. Check the plugin's API Logs. A 200 OK response means authentication succeeded.&lt;/p&gt;

&lt;p&gt;For a comprehensive breakdown of authentication errors across different APIs, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #3: The Required "Name" Field Is Missing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Copper returns a 400 Bad Request error. The response mentions a missing required field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Copper requires a &lt;code&gt;name&lt;/code&gt; field to create a lead. Without it, the API rejects the request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Verify your mapping.&lt;/strong&gt; In the plugin's JSON Payload section, ensure at least one CF7 field is mapped to the Copper &lt;code&gt;name&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Use a hidden field if needed.&lt;/strong&gt; If your form does not have a natural field for the lead name, add a hidden field with a default value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[hidden lead-name default:get "Website Lead"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Check the payload structure.&lt;/strong&gt; Your JSON should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-email]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"work"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copper's email field uses a nested object with &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;category&lt;/code&gt; properties. The category is typically &lt;code&gt;work&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For a deeper dive into JSON structure mapping, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #4: The Payload Structure Is Incorrect
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Copper returns a 400 Bad Request error with a message about invalid fields or unexpected structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Copper's API expects a specific JSON structure. Sending flat key-value pairs instead of the expected nested format will cause the request to fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Start with a minimal payload.&lt;/strong&gt; Use only the required fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-email]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"work"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Add fields one at a time.&lt;/strong&gt; Once the minimal payload works, add additional fields like &lt;code&gt;phone_numbers&lt;/code&gt;, &lt;code&gt;company_name&lt;/code&gt;, or &lt;code&gt;title&lt;/code&gt;. Test after each addition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Use the correct field names.&lt;/strong&gt; Copper's Lead object supports fields like &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;phone_numbers&lt;/code&gt;, &lt;code&gt;company_name&lt;/code&gt;, and &lt;code&gt;title&lt;/code&gt;. Field names must match exactly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Check the Content-Type header.&lt;/strong&gt; Ensure your request includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;application/json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Debugging Checklist
&lt;/h2&gt;

&lt;p&gt;Work through this sequence every time a lead fails to create:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Tells You&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Check the plugin's API Logs&lt;/td&gt;
&lt;td&gt;Was a request sent? What response code came back?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Verify the endpoint URL&lt;/td&gt;
&lt;td&gt;Does it target &lt;code&gt;/leads&lt;/code&gt;, not &lt;code&gt;/people&lt;/code&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Check all three auth headers&lt;/td&gt;
&lt;td&gt;Are X-PW-AccessToken, X-PW-Application, and X-PW-UserEmail present?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Confirm &lt;code&gt;name&lt;/code&gt; field is mapped&lt;/td&gt;
&lt;td&gt;Is the required field present in the payload?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Test with minimal payload&lt;/td&gt;
&lt;td&gt;Hardcode a &lt;code&gt;name&lt;/code&gt; value to isolate mapping issues.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Inspect the JSON structure&lt;/td&gt;
&lt;td&gt;Is &lt;code&gt;email&lt;/code&gt; a nested object with &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;category&lt;/code&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Beyond Leads: Creating Related Records
&lt;/h2&gt;

&lt;p&gt;Once you can create leads reliably, you may want to create related records. Copper's data model connects these objects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lead → Person:&lt;/strong&gt; When a lead is qualified, it can be converted to a Person.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person → Opportunity:&lt;/strong&gt; Opportunities represent deals and are associated with People.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copper also supports an upsert endpoint for leads (&lt;code&gt;/leads/upsert&lt;/code&gt;), which matches on email and updates existing records instead of creating duplicates. This is useful if you want repeat submissions to update the same lead rather than creating multiple records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Copper: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your Copper integration works, you might want to send the same submission to other tools: a different CRM, an email marketing platform, or a help desk system.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can create a Copper lead, add the contact to HubSpot, and send an SMS via Twilio, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Copper separates People and Leads into distinct endpoints.&lt;/strong&gt; Using &lt;code&gt;/people&lt;/code&gt; instead of &lt;code&gt;/leads&lt;/code&gt; creates the wrong record type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three authentication headers are required.&lt;/strong&gt; &lt;code&gt;X-PW-AccessToken&lt;/code&gt;, &lt;code&gt;X-PW-Application&lt;/code&gt; (fixed value &lt;code&gt;developer_api&lt;/code&gt;), and &lt;code&gt;X-PW-UserEmail&lt;/code&gt; must all be present and correct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;name&lt;/code&gt; field is required for lead creation.&lt;/strong&gt; Ensure it is mapped and never empty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The email field uses a nested structure.&lt;/strong&gt; Use &lt;code&gt;{"email": {"email": "...", "category": "work"}}&lt;/code&gt; rather than a flat string.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with a minimal payload.&lt;/strong&gt; Hardcode the &lt;code&gt;name&lt;/code&gt; value first. Once that works, add additional fields one at a time.&lt;/p&gt;

&lt;p&gt;With the correct endpoint, all three authentication headers, and a properly structured payload containing the required &lt;code&gt;name&lt;/code&gt; field, your CF7-to-Copper integration will create leads reliably.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>coppercrm</category>
      <category>crm</category>
    </item>
    <item>
      <title>CF7 to Keap (Infusionsoft) Not Adding Contacts: A Complete Troubleshooting Guide</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Fri, 16 Oct 2026 07:15:43 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-keap-infusionsoft-not-adding-contacts-a-complete-troubleshooting-guide-5e5o</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-keap-infusionsoft-not-adding-contacts-a-complete-troubleshooting-guide-5e5o</guid>
      <description>&lt;p&gt;You have built a Contact Form 7 form. You have connected it to Keap (formerly Infusionsoft) through the "Contact Form to Any API" plugin. You have entered your API credentials and mapped the fields. And yet, when a user submits the form, no contact appears in your Keap CRM.&lt;/p&gt;

&lt;p&gt;This is a frustratingly common problem. The good news? The issue is almost always one of four things, and each has a clear fix. This guide walks through the exact debugging process, common error patterns, and verified solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Data Flow
&lt;/h2&gt;

&lt;p&gt;When a user submits your CF7 form with Keap contact creation enabled, here is what should happen:&lt;/p&gt;

&lt;p&gt;User submits form → Plugin captures data → API request sent to Keap → Contact created&lt;/p&gt;

&lt;p&gt;When it breaks, the failure happens at one of the last two stages. Your first diagnostic tool is the &lt;strong&gt;plugin's API Logs page&lt;/strong&gt;. It shows whether a request was sent, the exact JSON payload, and the response from Keap. If no request appears in the logs, the problem is on the WordPress side. For a deeper breakdown of that specific scenario, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/" rel="noopener noreferrer"&gt;Contact Form 7 data not reaching the API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #1: The Infusionsoft Field Prefix Is Missing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; The plugin's logs are empty, or Keap returns an error. The form submits successfully, but no contact is created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Older CF7-to-Infusionsoft integrations require a specific field naming convention. Every form field that should be sent to Infusionsoft must be prefixed with &lt;code&gt;infusionsoft-&lt;/code&gt;. If your CF7 field is named &lt;code&gt;your-email&lt;/code&gt; but the integration expects &lt;code&gt;infusionsoft-email&lt;/code&gt;, the data never reaches the API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Rename your CF7 fields.&lt;/strong&gt; In the CF7 form editor, ensure every field you want to send to Keap uses the &lt;code&gt;infusionsoft-&lt;/code&gt; prefix.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[text* infusionsoft-first-name placeholder "First Name"]
[email* infusionsoft-email placeholder "Email"]
[tel infusionsoft-phone placeholder "Phone"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Update your field mappings.&lt;/strong&gt; In the API plugin's mapping configuration, match the prefixed CF7 field names to the Keap API parameters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Test with a minimal form.&lt;/strong&gt; Create a simple test form with only &lt;code&gt;infusionsoft-first-name&lt;/code&gt; and &lt;code&gt;infusionsoft-email&lt;/code&gt;. Submit it and check Keap for the new contact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important context:&lt;/strong&gt; If you are using the "Contact Form to Any API" plugin rather than a dedicated Infusionsoft add-on, you may be building the JSON payload manually. In that case, the prefix requirement does not apply, but the field mapping to Keap's API parameters must still be exact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #2: OAuth Authentication Is Not Configured Correctly
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Keap returns a 401 Unauthorized or "Developer Inactive" error. No contact is created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Keap uses OAuth 2.0 for API authentication . The API token must be valid, and the refresh token must be properly managed. A common issue is that the access token expires, and the integration cannot refresh it automatically.&lt;/p&gt;

&lt;p&gt;A Keap developer described this exact problem: "I am creating a new access token manually and replacing daily because I can't seem to figure out this OAuth process for simple API calls" . Another user reported getting "Developer Inactive" errors when trying to use a refreshed token .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Verify your OAuth credentials.&lt;/strong&gt; In Keap, navigate to Settings → API Settings. Copy your Client ID and Client Secret .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Generate a fresh API token.&lt;/strong&gt; According to Keap's OAuth documentation, you need to generate an API token through the developer portal .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Configure authentication in the plugin.&lt;/strong&gt; Add the Bearer token to the Authorization header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: Bearer YOUR_ACCESS_TOKEN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Handle token refresh.&lt;/strong&gt; Access tokens expire. If your plugin supports OAuth token refresh, ensure the refresh token is stored and used correctly. Note that when a refresh token is used, both the access and refresh tokens are invalidated, and a new pair is provided .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternative: Personal Access Tokens.&lt;/strong&gt; Keap also supports Personal Access Tokens (PAT), which are simpler for scripting integrations. You can create a PAT from Settings → API Settings .&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #3: The API Payload Structure Is Incorrect
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; Keap returns a 400 Bad Request error. The response mentions "Field is invalid" or "Bad Request."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Keap's API is strict about payload structure. The contact creation endpoint expects a specific JSON format with nested objects for email addresses and phone numbers.&lt;/p&gt;

&lt;p&gt;A Keap developer reported: "The error that I am receiving is only this: &lt;code&gt;{message: 'Field is invalid'}&lt;/code&gt;. It does not even say what the 'Field' is that is throwing the error!" .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Start with a minimal payload.&lt;/strong&gt; For Keap's REST API, a minimal contact creation payload looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"given_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[infusionsoft-first-name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"family_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[infusionsoft-last-name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email_addresses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[infusionsoft-email]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EMAIL1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Use the correct field names.&lt;/strong&gt; Keap's API uses specific field identifiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;given_name&lt;/code&gt; for first name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;family_name&lt;/code&gt; for last name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;email_addresses&lt;/code&gt; as an array with &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;field&lt;/code&gt; properties&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;phone_numbers&lt;/code&gt; as an array with &lt;code&gt;number&lt;/code&gt; and &lt;code&gt;field&lt;/code&gt; properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For phone numbers, you must include the &lt;code&gt;field&lt;/code&gt; property: &lt;code&gt;"field": "PHONE1"&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Check the Content-Type header.&lt;/strong&gt; Ensure your request includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Content-Type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;application/json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Use the correct endpoint.&lt;/strong&gt; The contact creation endpoint is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.infusionsoft.com/crm/rest/v1/contacts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note on region codes:&lt;/strong&gt; If you include an address, the &lt;code&gt;region&lt;/code&gt; field must use ISO region codes (e.g., &lt;code&gt;US-CO&lt;/code&gt; for Colorado), not plain text abbreviations .&lt;/p&gt;

&lt;p&gt;For a deeper dive into JSON structure mapping, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure #4: Duplicate Contact Detection Is Blocking Creation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you see:&lt;/strong&gt; No new contact appears in Keap. The API returns a 200 OK response instead of 201 Created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt; Keap's API may detect that a contact with the same email address already exists and return the existing contact instead of creating a new one. This is by design, but it can appear as a failure if you are checking for a new contact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Check the API response body.&lt;/strong&gt; A 200 response means a duplicate was found and the existing contact was returned. A 201 response means a new contact was created .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Use the &lt;code&gt;duplicate_option&lt;/code&gt; parameter.&lt;/strong&gt; Keap's API supports a &lt;code&gt;duplicate_option&lt;/code&gt; parameter to control duplicate detection. Available values include &lt;code&gt;EmailAndName&lt;/code&gt; and &lt;code&gt;EmailAndNameAndCompany&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Verify in Keap directly.&lt;/strong&gt; Search for the contact by email address. If it already exists, the integration is working correctly — it is just updating an existing record rather than creating a new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Checklist
&lt;/h2&gt;

&lt;p&gt;Work through this sequence every time a contact fails to create:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Tells You&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Check the plugin's API Logs&lt;/td&gt;
&lt;td&gt;Was a request sent? What response code came back?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Verify field naming prefix&lt;/td&gt;
&lt;td&gt;Are fields prefixed with &lt;code&gt;infusionsoft-&lt;/code&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Check OAuth token&lt;/td&gt;
&lt;td&gt;Is it valid and not expired?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Inspect the JSON payload&lt;/td&gt;
&lt;td&gt;Does it use the correct nested structure?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Check for duplicates&lt;/td&gt;
&lt;td&gt;Does the contact already exist in Keap?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Test with minimal payload&lt;/td&gt;
&lt;td&gt;Hardcode values to isolate structure issues.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a comprehensive breakdown of authentication errors across different APIs, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Contacts: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your Keap integration works, you might want to send the same submission to other tools: a different CRM, an email marketing platform, or a help desk system.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can create a Keap contact, add the lead to HubSpot, and send an SMS via Twilio, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Field naming may require the &lt;code&gt;infusionsoft-&lt;/code&gt; prefix.&lt;/strong&gt; Older integrations expect this convention. Rename your CF7 fields accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OAuth authentication must be configured correctly.&lt;/strong&gt; Access tokens expire. Ensure your integration can refresh them, or use a Personal Access Token for simpler setups .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keap's API expects a specific JSON structure.&lt;/strong&gt; Use &lt;code&gt;email_addresses&lt;/code&gt; and &lt;code&gt;phone_numbers&lt;/code&gt; arrays with the correct field identifiers .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplicate detection may return 200 instead of 201.&lt;/strong&gt; Check the response code and verify in Keap whether the contact already exists .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with a minimal payload.&lt;/strong&gt; Hardcode a test contact first. Once that creates a record in Keap, add additional fields one at a time.&lt;/p&gt;

&lt;p&gt;With the correct field naming, valid OAuth credentials, and a properly structured payload, your CF7-to-Keap integration will create contacts reliably.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>infusionsoft</category>
      <category>apiintegration</category>
    </item>
    <item>
      <title>CF7 Multi-Step Forms Not Sending All Fields to the API - Why Step Data Goes Missing</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Wed, 14 Oct 2026 10:32:08 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-multi-step-forms-not-sending-all-fields-to-the-api-why-step-data-goes-missing-5h2b</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-multi-step-forms-not-sending-all-fields-to-the-api-why-step-data-goes-missing-5h2b</guid>
      <description>&lt;p&gt;You built a multi-step Contact Form 7 form. It works perfectly for email notifications. Every field arrives in your inbox. But when you connect it to an API through the "Contact Form to Any API" plugin, the data is incomplete. Fields from earlier steps are empty. Or worse, the API payload contains nothing at all.&lt;/p&gt;

&lt;p&gt;This is one of the most frustrating problems in CF7 API integrations. The form works. The email works. But the API receives missing or empty data.&lt;/p&gt;

&lt;p&gt;The good news is that this problem has a specific cause and a clear fix. This guide explains exactly why step data goes missing and how to ensure every field reaches your API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Multi-Step Forms Break API Integrations
&lt;/h2&gt;

&lt;p&gt;The root cause is how CF7 multi-step plugins work under the hood.&lt;/p&gt;

&lt;p&gt;Multi-step form plugins split a single CF7 form across multiple pages. The plugin uses browser cookies to store data from earlier steps and pass it to the final submission. This works fine for email notifications because the final form submission contains all the accumulated data.&lt;/p&gt;

&lt;p&gt;But when you connect an API integration plugin, the data flow changes. The API plugin intercepts the form submission at a different point in the process. If the multi-step plugin has not yet assembled all the step data into the final submission, the API receives only the fields from the current step.&lt;/p&gt;

&lt;p&gt;A WordPress user described exactly this scenario: "I used to connect Contact Form 7 with Microsoft Flow using the 'CF7 to Zapier' plugin, but after changing my form using Contact Form 7 Multi-Step Forms the data isn't passed through anymore. The API works as it should be, but every field is empty."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cookie Storage Limit Problem
&lt;/h2&gt;

&lt;p&gt;There is a second, less obvious cause: browser cookie size limits.&lt;/p&gt;

&lt;p&gt;Multi-step plugins store step data in cookies. Cookies have a maximum size of approximately 4KB. If your form has many fields or long text inputs, the cookie can exceed this limit. When that happens, data from earlier steps is silently dropped.&lt;/p&gt;

&lt;p&gt;A WordPress support thread documented this exact issue: "This plugin uses cookies to store the information from step to step and cookies do have a limit to the amount of information it can hold." The user reported that fields would go missing at random, sometimes step 6, sometimes step 7, and the pattern depended on how much data was entered in earlier steps.&lt;/p&gt;

&lt;p&gt;This is why the problem appears intermittent. Short submissions work. Long submissions fail. The cookie limit is the hidden threshold.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Field Trap
&lt;/h2&gt;

&lt;p&gt;Even when the cookie storage works correctly, there is another layer of complexity: conditional fields within multi-step forms.&lt;/p&gt;

&lt;p&gt;If your form uses conditional logic, fields that appear based on earlier selections may not be included in the final submission at all. A WordPress user reported: "The multistep can catch all the a-1 and b-1 info, but will get null value with a-2 and b-2 as there is no value display on the confirmation letter."&lt;/p&gt;

&lt;p&gt;The solution for conditional fields in multi-step forms is to add hidden fields on the later steps that mirror the conditional field names. This ensures the multi-step plugin has a field to populate with the earlier step's data.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the API Plugin Handles This
&lt;/h2&gt;

&lt;p&gt;The "Contact Form to Any API" plugin provides conditional field logic in its API configuration settings. This allows you to define which fields appear in the API payload based on conditions.&lt;/p&gt;

&lt;p&gt;However, the plugin cannot fix a problem that occurs before the form submission reaches it. If the multi-step plugin fails to assemble the full data due to cookie limits or missing hidden fields, the API plugin has nothing to work with.&lt;/p&gt;

&lt;p&gt;The integration requires two layers of configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: The CF7 Multi-Step Form.&lt;/strong&gt; Ensure all fields are properly carried through steps using hidden fields where needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: The API Plugin.&lt;/strong&gt; Configure conditional field logic to include only the fields that are actually populated for each submission path.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Checklist
&lt;/h2&gt;

&lt;p&gt;Work through this sequence when multi-step form fields are missing from the API payload:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Tells You&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Check the plugin's API Logs&lt;/td&gt;
&lt;td&gt;Does the logged payload show missing fields?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Test with a short submission&lt;/td&gt;
&lt;td&gt;If short submissions work, the cookie limit is the issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Test with a long submission&lt;/td&gt;
&lt;td&gt;If long submissions fail, cookie storage is the bottleneck&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Add hidden fields to later steps&lt;/td&gt;
&lt;td&gt;Ensures conditional field data is carried through&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Verify field names are unique&lt;/td&gt;
&lt;td&gt;Duplicate names cause data overwrites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Check for page caching&lt;/td&gt;
&lt;td&gt;Caching can prevent form data from passing correctly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a deeper dive into JSON structure mapping and conditional payload construction, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solutions That Work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Solution 1: Reduce Form Data Volume
&lt;/h3&gt;

&lt;p&gt;If you are hitting the cookie limit, reduce the amount of data stored per step. Use shorter field names, limit textarea lengths, and avoid storing unnecessary data in the form.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 2: Use Hidden Fields for Conditional Data
&lt;/h3&gt;

&lt;p&gt;For conditional fields within multi-step forms, add hidden fields on the final step that mirror the conditional field names. The multi-step plugin will populate these hidden fields with the earlier step's data.&lt;/p&gt;

&lt;p&gt;Example hidden field on the final step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[hidden tour-bhutan]
[hidden tour-nepal]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the conditional field values are included in the final submission.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 3: Switch to a Database-Backed Multi-Step Plugin
&lt;/h3&gt;

&lt;p&gt;Some multi-step plugins save submission data to the WordPress database instead of cookies. This eliminates the cookie size limit entirely. The API plugin can then retrieve the full submission data from the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 4: Disable Page Caching for Form Pages
&lt;/h3&gt;

&lt;p&gt;Caching plugins can interfere with multi-step form data flow. Exclude pages containing multi-step forms from caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Two-Layer Configuration Approach
&lt;/h2&gt;

&lt;p&gt;Once the form data is flowing correctly, configure the API plugin's conditional logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the CF7 form:&lt;/strong&gt; Use conditional fields and hidden fields to ensure all data is captured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the API plugin:&lt;/strong&gt; Use conditional field logic (Equals / Not Equals) to include only populated fields in the payload. This prevents empty fields from cluttering your CRM.&lt;/p&gt;

&lt;p&gt;For a comprehensive breakdown of authentication and error handling across different APIs, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Multi-Step: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your multi-step form data flows correctly to your API, you might want to send the same submission to other tools: a CRM, an email marketing platform, or a help desk system.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can create a CRM contact, send an SMS via Twilio, and create a Zendesk ticket, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Multi-step plugins store step data in cookies.&lt;/strong&gt; Cookies have a size limit. Long forms or many fields can exceed it, causing silent data loss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conditional fields need hidden field mirrors.&lt;/strong&gt; Add hidden fields on later steps to ensure conditional field data is carried through to the final submission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The API plugin cannot fix missing form data.&lt;/strong&gt; It can only map what the form submission provides. Ensure the multi-step plugin delivers complete data first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Page caching can break multi-step data flow.&lt;/strong&gt; Exclude form pages from caching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test with short and long submissions.&lt;/strong&gt; If short works and long fails, you are hitting the cookie limit.&lt;/p&gt;

&lt;p&gt;With the right hidden fields, reduced data volume, and proper conditional mapping in the API plugin, your multi-step CF7 forms will deliver complete data to your API on every submission.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>api</category>
      <category>apiintegration</category>
    </item>
    <item>
      <title>CF7 eCommerce Contact Form to Order/ERP API: A Complete Implementation Guide</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Tue, 13 Oct 2026 11:26:07 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-ecommerce-contact-form-to-ordererp-api-a-complete-implementation-guide-1fgf</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-ecommerce-contact-form-to-ordererp-api-a-complete-implementation-guide-1fgf</guid>
      <description>&lt;p&gt;An eCommerce store often needs more than just a standard "contact us" form. Customers submit custom order requests, ask about bulk pricing, or inquire about product availability. Manually copying this data from form submission emails into your ERP or order management system is slow, error-prone, and does not scale.&lt;/p&gt;

&lt;p&gt;This guide walks through how to automate the process using Contact Form 7 and the "Contact Form to Any API" plugin, sending eCommerce inquiries directly into your order or ERP API as structured records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why eCommerce Forms Need ERP Integration
&lt;/h2&gt;

&lt;p&gt;eCommerce contact forms generate data that belongs in your order pipeline, not in an inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom order requests.&lt;/strong&gt; A customer filling out a form for a custom product needs that request to become an order or a quote in your system, with all the product details attached.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulk and B2B inquiries.&lt;/strong&gt; Wholesale customers often submit forms with quantity requests. This data needs to flow into your ERP for pricing, inventory checks, and fulfillment planning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product-specific questions.&lt;/strong&gt; When a customer asks about a specific SKU, that inquiry should be linked to the product record in your ERP for the sales team to follow up.&lt;/p&gt;

&lt;p&gt;Without automation, every inquiry is a manual data entry task. With automation, every submission becomes a structured record ready for processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: CF7 → API Plugin → ERP/Order API
&lt;/h2&gt;

&lt;p&gt;The integration follows a straightforward path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CF7 Form → Contact Form to Any API → ERP/Order API → Record Created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin sends form submissions directly to your API endpoint using secure methods. It supports any endpoint that accepts POST or GET requests with structured data, including ERP systems like Odoo, Sage CRM, and custom order management APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  eCommerce Form Fields That Matter
&lt;/h2&gt;

&lt;p&gt;A custom order or product inquiry form needs specific fields that map to your ERP or order API. Here is a field set designed for eCommerce integration:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CF7 Field&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;ERP/Order API Mapping&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;your-name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Customer name&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;contact_name&lt;/code&gt; or &lt;code&gt;customer_name&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;your-email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Email address&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;email_from&lt;/code&gt; or &lt;code&gt;customer_email&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;your-phone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Phone number&lt;/td&gt;
&lt;td&gt;&lt;code&gt;phone&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;product-sku&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Product identifier&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;product_id&lt;/code&gt; or &lt;code&gt;item_code&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quantity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requested quantity&lt;/td&gt;
&lt;td&gt;&lt;code&gt;product_qty&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;product-notes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Customization details&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;description&lt;/code&gt; or &lt;code&gt;note&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;budget&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Budget indication (for quotes)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;expected_revenue&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;your-message&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Additional message&lt;/td&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These fields map directly to standard order and ERP lead fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the API Integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Configure the ERP/Order API Endpoint
&lt;/h3&gt;

&lt;p&gt;In the "Contact Form to Any API" plugin settings, enter your ERP's API endpoint. For Odoo, this is typically the JSON-RPC or XML-RPC endpoint. For other systems, consult the API documentation for the order or lead creation endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Map CF7 Fields to ERP Fields
&lt;/h3&gt;

&lt;p&gt;The plugin provides field mapping between CF7 form fields and the API payload parameters. The mapping syntax follows this pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Form Fields → API Parameters

[your-name]    → contact_name
[your-email]   → email_from
[product-sku]  → product_id
[quantity]     → product_qty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a deeper dive into JSON structure mapping, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Configure Authentication
&lt;/h3&gt;

&lt;p&gt;ERP systems often require robust authentication. The plugin supports Basic Auth, API Key, and Bearer Token headers. For OAuth 2.0 flows, the PRO add-on handles automatic token renewal, ensuring integrations continue running without interruption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A critical note on Basic Auth:&lt;/strong&gt; The plugin's header field is a static textarea. PHP functions like &lt;code&gt;base64_encode()&lt;/code&gt; do not execute when placed directly in this field. You must manually encode your credentials before pasting them into the header.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Build the JSON Payload
&lt;/h3&gt;

&lt;p&gt;The payload structure depends on your ERP's API requirements. A typical custom order or quote request payload might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Custom Order - [product-sku]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contact_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email_from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-email]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-phone]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"product_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[product-sku]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"product_qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[quantity]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[product-notes]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lead"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  eCommerce Specific Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Product SKU as Order Context
&lt;/h3&gt;

&lt;p&gt;When a customer submits an inquiry about a specific product, the SKU should be included in the record. This allows your ERP to link the inquiry to the correct product for pricing, inventory, and fulfillment checks. Map a hidden CF7 field containing the product SKU to the ERP's product field.&lt;/p&gt;

&lt;p&gt;Example hidden field in CF7:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[hidden product-sku default:get "sku"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This captures the SKU from the page URL or a shortcode parameter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quantity and Pricing Logic
&lt;/h3&gt;

&lt;p&gt;For bulk or custom orders, the quantity field is critical. Your ERP may need to calculate pricing based on quantity tiers or check available inventory. The API payload should include the requested quantity so the ERP can perform these calculations automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Order vs Lead: Which Record Type?
&lt;/h3&gt;

&lt;p&gt;Some ERP systems distinguish between a "lead" (unqualified inquiry) and an "order" (confirmed purchase). For eCommerce contact forms, the appropriate record type depends on your workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lead/Quote:&lt;/strong&gt; If the customer is inquiring about a product and needs a price quote before committing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Order:&lt;/strong&gt; If the form is a direct order submission with all required payment and shipping details.&lt;/p&gt;

&lt;p&gt;The payload's &lt;code&gt;type&lt;/code&gt; field determines which record is created in the ERP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Challenges and Solutions
&lt;/h2&gt;

&lt;p&gt;ERP integrations have specific pitfalls. Here are the most common ones and how to avoid them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication Failures.&lt;/strong&gt; ERP systems often use OAuth 2.0 or token-based access. If tokens expire or are misconfigured, data transmission halts without warning. Solution: Use token refresh logic and test authentication during setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ERP System Downtime.&lt;/strong&gt; If your ERP goes offline for maintenance, form submissions cannot be delivered. Solution: Set up a retry mechanism that queues failed submissions and resends them once the ERP is back online.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Field Mapping Errors.&lt;/strong&gt; Data gets lost or misrouted when fields between CF7 and the ERP are not accurately mapped. Solution: Perform field audits before going live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Format Mismatches.&lt;/strong&gt; ERPs have strict requirements for data format. Dates might need ISO standards, or dropdown values must match predefined codes. Solution: Use transformation logic to sanitize and validate data before submission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing Required Fields.&lt;/strong&gt; If the ERP API requires a field that is not mapped or is left empty, the request fails. Solution: Ensure all required fields are mapped and never empty.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Checklist
&lt;/h2&gt;

&lt;p&gt;Work through this sequence when an eCommerce form fails to create an ERP record:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What It Tells You&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Check the plugin's API Logs&lt;/td&gt;
&lt;td&gt;Was a request sent? What response code came back?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Inspect the response code&lt;/td&gt;
&lt;td&gt;401 = Auth issue. 422 = Payload issue. 500 = ERP server error.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Verify Basic Auth encoding&lt;/td&gt;
&lt;td&gt;Is the header manually Base64 encoded?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Check the endpoint URL&lt;/td&gt;
&lt;td&gt;Does it target the correct ERP API endpoint?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Verify field mapping&lt;/td&gt;
&lt;td&gt;Are all required fields mapped and populated?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Test with minimal payload&lt;/td&gt;
&lt;td&gt;Hardcode values to isolate structure issues from mapping issues.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Test credentials in Postman&lt;/td&gt;
&lt;td&gt;Isolates plugin issues from API issues.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a comprehensive breakdown of authentication errors across different APIs, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond ERP: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your ERP integration works, you might want to send the same submission to other tools: an email marketing platform, a help desk system, or a notification service.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can create an ERP record, subscribe the contact to Mailchimp, and send an SMS notification via Twilio, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;eCommerce form data belongs in your ERP, not your inbox.&lt;/strong&gt; Automate the flow from CF7 submission to ERP record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication is the most fragile part of the integration.&lt;/strong&gt; Use token refresh for OAuth systems, and manually Base64-encode Basic Auth credentials before pasting them into the plugin's header field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Field mapping must be exact.&lt;/strong&gt; Match CF7 field names to your ERP's API field names precisely. Perform field audits before going live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with a minimal payload.&lt;/strong&gt; Hardcode a test record first. Once that works, add product-specific fields one at a time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plan for failures.&lt;/strong&gt; ERP downtime and API errors will happen. Implement retry logic and monitor the plugin's logs to catch issues early.&lt;/p&gt;

&lt;p&gt;With the correct API configuration, field mapping, and authentication, your CF7 eCommerce forms will deliver custom order requests and product inquiries directly into your ERP pipeline. No manual data entry, no missed opportunities, no delay.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>apiintegration</category>
      <category>erp</category>
    </item>
    <item>
      <title>CF7 Real Estate Lead Capture to CRM: A Complete Implementation Guide</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Sat, 10 Oct 2026 08:02:04 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-real-estate-lead-capture-to-crm-a-complete-implementation-guide-4ajk</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-real-estate-lead-capture-to-crm-a-complete-implementation-guide-4ajk</guid>
      <description>&lt;p&gt;Real estate websites live and die by lead response time. When a potential buyer submits an inquiry about a property, every minute of delay reduces the chance of conversion. Manual copy-pasting from form submission emails into a CRM is not just inefficient; it directly costs you deals.&lt;/p&gt;

&lt;p&gt;This guide walks through how to build an automated real estate lead capture pipeline using Contact Form 7 and the "Contact Form to Any API" plugin, sending every submission directly into your CRM as a structured lead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Real Estate Lead Capture Needs Automation
&lt;/h2&gt;

&lt;p&gt;Real estate lead generation has unique characteristics that make automation essential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Volume and urgency.&lt;/strong&gt; A single property listing can generate dozens of inquiries per day. Each inquiry needs a timely response. Manual processing cannot scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured data requirements.&lt;/strong&gt; Real estate leads need specific fields: name, email, phone, property ID, inquiry type (schedule a viewing, request information, make an offer), budget range, and timeline. Sending this as an unstructured email means manual data entry into the CRM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Team assignment.&lt;/strong&gt; Most real estate teams route leads by property type, price range, or location. Automated CRM creation allows immediate assignment rules to fire.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: CF7 → API Plugin → CRM
&lt;/h2&gt;

&lt;p&gt;The cleanest approach for real estate lead capture uses three components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CF7 Form (property inquiry) → Contact Form to Any API → CRM Lead Creation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the recommended practical approach for eliminating manual data entry. The setup is straightforward: add your CRM API endpoint in the plugin settings, map your form fields like name, email, and phone to CRM lead fields, and every form submission automatically creates a lead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Estate Form Fields That Matter
&lt;/h2&gt;

&lt;p&gt;A real estate inquiry form should capture more than just name and email. Here is a field set that supports CRM lead creation and routing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CF7 Field&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;CRM Mapping&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;your-name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lead name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;contact_name&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;your-email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Email address&lt;/td&gt;
&lt;td&gt;&lt;code&gt;email_from&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;your-phone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Phone number&lt;/td&gt;
&lt;td&gt;&lt;code&gt;phone&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;property-id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which property they inquired about&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;description&lt;/code&gt; or custom field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inquiry-type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Schedule viewing / Request info / Make offer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lead_source&lt;/code&gt; or &lt;code&gt;description&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;budget-range&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Budget indication&lt;/td&gt;
&lt;td&gt;Custom field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeline&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How soon they want to buy or rent&lt;/td&gt;
&lt;td&gt;Custom field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;your-message&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Free-text message&lt;/td&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These fields map directly to standard CRM lead fields: &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;contact_name&lt;/code&gt;, &lt;code&gt;email_from&lt;/code&gt;, &lt;code&gt;phone&lt;/code&gt;, and &lt;code&gt;description&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the API Integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Configure the CRM Endpoint
&lt;/h3&gt;

&lt;p&gt;In the "Contact Form to Any API" plugin settings, enter your CRM's API endpoint. For most CRMs, this is the REST API endpoint for lead creation. Consult your CRM's API documentation for the exact URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Map CF7 Fields to CRM Fields
&lt;/h3&gt;

&lt;p&gt;The plugin provides field mapping between CF7 form fields and the API payload parameters. The mapping works like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Form Fields → API Parameters

[your-name]    → contact_name
[your-email]   → email_from
[your-phone]   → phone
[your-message] → description
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For real estate, you would extend this with property-specific fields like &lt;code&gt;property-id&lt;/code&gt;, &lt;code&gt;inquiry-type&lt;/code&gt;, and &lt;code&gt;budget-range&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Configure Authentication
&lt;/h3&gt;

&lt;p&gt;Most CRM APIs require authentication. The plugin supports Bearer Token, API Key, and Basic Auth headers. Check your CRM's documentation for the required method. For a full breakdown of authentication options, see this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;Contact Form 7 API authentication errors&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Build the JSON Payload
&lt;/h3&gt;

&lt;p&gt;The payload structure depends on your CRM's API requirements. A typical lead creation payload for real estate would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Website Lead - [your-name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contact_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email_from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-email]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-phone]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[your-message]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lead"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a deeper dive into JSON structure mapping, read this guide on &lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;Contact Form 7 JSON mapping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Estate Specific Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Property ID as Lead Context
&lt;/h3&gt;

&lt;p&gt;When a visitor inquires about a specific property, the property ID should be included in the lead. In the CRM, this allows the sales team to see exactly which property the lead is interested in. Map a hidden CF7 field containing the property ID to a CRM custom field or the description field.&lt;/p&gt;

&lt;p&gt;Example hidden field in CF7:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[hidden property-id default:get "property-id"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This captures the property ID from the page URL or a shortcode parameter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inquiry Type Routing
&lt;/h3&gt;

&lt;p&gt;Different inquiry types require different follow-up. A "schedule viewing" request needs a calendar link. A "make offer" request needs immediate escalation. By mapping &lt;code&gt;inquiry-type&lt;/code&gt; to a CRM field, you can configure CRM automation rules to route leads appropriately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Duplicate Prevention
&lt;/h3&gt;

&lt;p&gt;Real estate leads often submit multiple inquiries. You can add a hidden CF7 field with a unique timestamp or use CRM-side deduplication rules to prevent duplicate records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Lead Creation: What Happens Next
&lt;/h2&gt;

&lt;p&gt;Once leads are in the CRM, real estate teams need:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instant assignment.&lt;/strong&gt; CRM automation rules assign leads to agents based on property type, location, or round-robin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Follow-up sequences.&lt;/strong&gt; Email and SMS sequences trigger based on lead source and inquiry type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pipeline tracking.&lt;/strong&gt; Leads move through stages: New → Contacted → Viewing Scheduled → Offer Made → Closed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reporting.&lt;/strong&gt; Which properties generate the most leads? Which agents convert best? What is the average response time?&lt;/p&gt;

&lt;p&gt;All of these depend on clean, structured lead data entering the CRM. That is what the CF7 → API integration delivers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Challenges and Solutions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API authentication fails&lt;/td&gt;
&lt;td&gt;Verify the correct auth method (Bearer vs API Key vs Basic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Required fields missing&lt;/td&gt;
&lt;td&gt;Ensure all CRM-required fields are mapped and never empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong data format&lt;/td&gt;
&lt;td&gt;Match the payload structure to CRM API documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Property ID not captured&lt;/td&gt;
&lt;td&gt;Add a hidden CF7 field populated from the page URL or shortcode parameter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate leads&lt;/td&gt;
&lt;td&gt;Use unique identifiers or CRM-side deduplication rules&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Beyond Real Estate: One Form, Multiple Destinations
&lt;/h2&gt;

&lt;p&gt;Once your real estate lead capture works, you might want to send the same form submission to other tools: an email marketing platform, a help desk system, or an SMS notification service.&lt;/p&gt;

&lt;p&gt;A good connector plugin supports multiple API integrations from a single form submission. You can create a CRM lead, subscribe the contact to Mailchimp, and send an SMS notification via Twilio, all from one CF7 submission. For a cost comparison, see this article on &lt;a href="https://www.contactformtoapi.com/zapier-vs-contact-form-to-api-plugin-cost-comparison/" rel="noopener noreferrer"&gt;Zapier vs a dedicated API plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Real estate lead capture requires structured data, not email text.&lt;/strong&gt; Map name, email, phone, property ID, and inquiry type to CRM fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CF7 → API → CRM architecture eliminates manual data entry.&lt;/strong&gt; Every form submission creates a CRM lead automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Field mapping is the core configuration.&lt;/strong&gt; Match CF7 field names to your CRM's API field names exactly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with a minimal payload.&lt;/strong&gt; Hardcode a test lead first. Once that creates a CRM record, add property-specific fields one at a time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CRM automation depends on clean data.&lt;/strong&gt; Assignment rules, follow-up sequences, and reporting all require structured lead fields from the form.&lt;/p&gt;

&lt;p&gt;With the correct API configuration, field mapping, and property-specific context, your CF7 real estate forms will deliver leads directly into your CRM pipeline. No manual data entry, no missed inquiries, no delay.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>contactform7</category>
      <category>apiintegration</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
