<?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 Trello: Cards Not Being Created Every Cause Explained</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Thu, 10 Sep 2026 06:30:07 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-trello-cards-not-being-created-every-cause-explained-2l1b</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-trello-cards-not-being-created-every-cause-explained-2l1b</guid>
      <description>&lt;p&gt;Trello is one of the simplest project management tools and its API is one of the most straightforward to call. Yet CF7 to Trello integrations fail constantly, usually for the same handful of reasons that are never clearly documented anywhere.&lt;/p&gt;

&lt;p&gt;This post covers every cause of CF7 submissions not creating Trello cards, with direct API calls you can use to verify each piece before connecting your form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 1: API Key and Token Confusion
&lt;/h2&gt;

&lt;p&gt;Trello uses a two-part authentication system that trips up most developers on first setup.&lt;/p&gt;

&lt;p&gt;You need two separate values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Key:&lt;/strong&gt; Found at &lt;code&gt;trello.com/power-ups/admin&lt;/code&gt; after creating a Power-Up. This is your application identifier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Token:&lt;/strong&gt; Generated separately by authorising your Power-Up to access your Trello account. This is the user-level access grant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API Key alone cannot make API calls. The Token alone cannot make API calls. Both are required on every request.&lt;/p&gt;

&lt;p&gt;The correct API call format 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.trello.com/1/cards?key=YOUR_API_KEY&amp;amp;token=YOUR_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or as headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.trello.com/1/cards"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"key=YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"token=YOUR_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"idList=YOUR_LIST_ID"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"name=Test Card from CF7"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"desc=This is a test submission"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If either value is missing or swapped, Trello returns &lt;code&gt;invalid key&lt;/code&gt; or &lt;code&gt;invalid token&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to generate a token:&lt;/strong&gt; After generating your API Key, go to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://trello.com/1/authorize?expiration=never&amp;amp;scope=read,write&amp;amp;response_type=token&amp;amp;name=CF7+Integration&amp;amp;key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_API_KEY&lt;/code&gt; with your actual key. Authorise and copy the token from the resulting page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 2: Using Board ID Instead of List ID
&lt;/h2&gt;

&lt;p&gt;Cards in Trello are created inside Lists, not inside Boards. The API endpoint for creating a card requires a List ID (&lt;code&gt;idList&lt;/code&gt;), not a Board ID.&lt;/p&gt;

&lt;p&gt;Board IDs and List IDs look identical — both are 24-character alphanumeric strings. This is where most configuration errors happen. The plugin asks for a List ID. The user looks up their Board URL, copies the Board ID, and pastes it in. The API returns &lt;code&gt;invalid id&lt;/code&gt; or creates nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find your List ID:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get all lists on a board&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://api.trello.com/1/boards/YOUR_BOARD_ID/lists?key=YOUR_API_KEY&amp;amp;token=YOUR_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each list in the response has an &lt;code&gt;id&lt;/code&gt; field. That 24-character string is what goes in the &lt;code&gt;idList&lt;/code&gt; parameter when creating a card.&lt;/p&gt;

&lt;p&gt;Find your Board ID from the board URL: &lt;code&gt;trello.com/b/BOARD_ID/board-name&lt;/code&gt;. The short code after &lt;code&gt;/b/&lt;/code&gt; is not the full ID. To get the full Board ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.trello.com/1/members/me/boards?key=YOUR_API_KEY&amp;amp;token=YOUR_TOKEN&amp;amp;fields=id,name"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cause 3: The Card Description Can Only Hold One Mapped Field in Free Plugins
&lt;/h2&gt;

&lt;p&gt;Unlike ClickUp which has custom fields, Trello cards have a limited structure: Name, Description, Labels, Due Date, and Members. Most CF7 to Trello integrations map form data to the card Name and Description.&lt;/p&gt;

&lt;p&gt;The Description field in Trello accepts plain text or Markdown. If your form has multiple fields (name, email, phone, message), you need to concatenate them into a single Description string.&lt;/p&gt;

&lt;p&gt;Many free CF7 Trello plugins only allow mapping one CF7 field to the Description. If you have a form with five fields, four of them go nowhere. The card is created with just the name and one field value.&lt;/p&gt;

&lt;p&gt;The correct approach when using the Trello API directly is to build the description as a multi-line 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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"idList"&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_LIST_ID"&lt;/span&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;"Enquiry from [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;"desc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**Name:** [your-name]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;**Email:** [your-email]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;**Phone:** [your-phone]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;**Message:** [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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trello renders Markdown in card descriptions, so &lt;code&gt;**Name:**&lt;/code&gt; renders as bold in the Trello UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 4: Token Expired or Insufficient Scope
&lt;/h2&gt;

&lt;p&gt;When generating the Trello token, you choose an expiration and scope. If the expiration was set to 30 days and it has now been more than 30 days since setup, the token has expired. Every API call returns &lt;code&gt;invalid token&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The scope must include &lt;code&gt;write&lt;/code&gt; to create cards. A token generated with &lt;code&gt;scope=read&lt;/code&gt; can fetch board and list data but cannot create, update, or delete anything.&lt;/p&gt;

&lt;p&gt;Always generate tokens with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;expiration=never&lt;/code&gt; for server integrations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scope=read,write&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your existing token has expired or has read-only scope, generate a new one using the URL in Cause 1 above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting CF7 to Trello Directly
&lt;/h2&gt;

&lt;p&gt;Trello's card creation API is genuinely one of the simplest in the project management space. You do not need a dedicated plugin. &lt;a href="https://www.contactformtoapi.com/contact-form-7-third-party-integration-guide/" rel="noopener noreferrer"&gt;Contact Form to API&lt;/a&gt; handles the POST request from the WordPress dashboard.&lt;/p&gt;

&lt;p&gt;Configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint:&lt;/strong&gt; &lt;code&gt;https://api.trello.com/1/cards&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method:&lt;/strong&gt; POST&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Body:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&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;"key"&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_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token"&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_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"idList"&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_LIST_ID"&lt;/span&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;"Enquiry from [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;"desc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**Email:** [your-email]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;**Phone:** [your-phone]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;**Message:** [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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every CF7 field in brackets gets replaced with the actual submitted value before the request is sent. The response log shows you whether Trello created the card and what the card ID is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Diagnosis
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Test your API key and token&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://api.trello.com/1/members/me?key=YOUR_KEY&amp;amp;token=YOUR_TOKEN"&lt;/span&gt;
&lt;span class="c"&gt;# Expect: your Trello user object&lt;/span&gt;
&lt;span class="c"&gt;# "invalid key" = API key wrong&lt;/span&gt;
&lt;span class="c"&gt;# "invalid token" = token wrong, expired, or wrong scope&lt;/span&gt;

&lt;span class="c"&gt;# 2. Get your board's lists to find List IDs&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://api.trello.com/1/boards/YOUR_BOARD_ID/lists?key=YOUR_KEY&amp;amp;token=YOUR_TOKEN"&lt;/span&gt;

&lt;span class="c"&gt;# 3. Create a test card&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.trello.com/1/cards?key=YOUR_KEY&amp;amp;token=YOUR_TOKEN&amp;amp;idList=YOUR_LIST_ID&amp;amp;name=Test+Card"&lt;/span&gt;
&lt;span class="c"&gt;# Expect: card object with "id" field&lt;/span&gt;
&lt;span class="c"&gt;# "invalid id" = wrong List ID (possibly using Board ID instead)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Cause&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;&lt;code&gt;invalid key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wrong API key&lt;/td&gt;
&lt;td&gt;Get key from &lt;code&gt;trello.com/power-ups/admin&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;invalid token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Token wrong, expired, or read-only&lt;/td&gt;
&lt;td&gt;Regenerate with &lt;code&gt;expiration=never&amp;amp;scope=read,write&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;invalid id&lt;/code&gt; on card creation&lt;/td&gt;
&lt;td&gt;Board ID used instead of List ID&lt;/td&gt;
&lt;td&gt;Fetch lists from board, use the list &lt;code&gt;id&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Card created but only has name&lt;/td&gt;
&lt;td&gt;Free plugin limitation on description&lt;/td&gt;
&lt;td&gt;Use direct API with multi-field description string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cards stopped being created&lt;/td&gt;
&lt;td&gt;Token expired&lt;/td&gt;
&lt;td&gt;Check token age, regenerate if past expiration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>wordpress</category>
      <category>trello</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CF7 to ClickUp Tasks Not Being Created - Four Causes and How to Fix Each</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Wed, 09 Sep 2026 11:19:53 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-clickup-tasks-not-being-created-four-causes-and-how-to-fix-each-1cf1</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-clickup-tasks-not-being-created-four-causes-and-how-to-fix-each-1cf1</guid>
      <description>&lt;p&gt;You connected Contact Form 7 to ClickUp using an integration plugin. You set up the task creation. You tested the form. Nothing appeared in ClickUp.&lt;/p&gt;

&lt;p&gt;Or maybe it was working, you added a new field to your form, and now the integration fails every time with an error about an invalid field value.&lt;/p&gt;

&lt;p&gt;Both situations happen constantly with CF7 to ClickUp integrations. Here is what is actually going wrong in each case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 1: Field Type Mismatch - the &lt;code&gt;FIELD_010&lt;/code&gt; Error
&lt;/h2&gt;

&lt;p&gt;This was the exact error in a WordPress support forum thread:&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;"err"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Value is not a valid URL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ECODE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FIELD_010"&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;The developer had added a new field to their WordPress form and mapped it to a ClickUp custom field. The problem: the ClickUp field was configured as a URL type. The CF7 field was a plain text field. When the integration sent a text value to a URL custom field, ClickUp rejected it with &lt;code&gt;FIELD_010&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;ClickUp has strict type validation on custom fields. If you map a CF7 text field to a ClickUp Number field, a URL field, or a Date field, ClickUp will reject the value silently or with the &lt;code&gt;FIELD_010&lt;/code&gt; error depending on your plugin's error handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to check:&lt;/strong&gt; In ClickUp, open the List where tasks are being created and look at the custom fields. Each field has a type icon next to the name (text, number, URL, date, email, etc.). Match your CF7 field type to the ClickUp custom field type exactly. For most form fields, ClickUp's "Text" custom field type is the safest destination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 2: Wrong Location in ClickUp's Four-Level Hierarchy
&lt;/h2&gt;

&lt;p&gt;ClickUp organises work in four nested levels: Workspace → Space → Folder → List. Tasks are created inside Lists. When setting up a CF7 to ClickUp integration, you must select all four levels correctly before the plugin knows where to create the task.&lt;/p&gt;

&lt;p&gt;The most common mistake: selecting the Space or Folder instead of the List. Tasks cannot be created at the Space or Folder level in ClickUp's API. If your plugin is trying to create a task at the wrong level, the API returns an error that the plugin may swallow silently.&lt;/p&gt;

&lt;p&gt;A second common mistake: the List ID in your plugin settings is stale. If you renamed or moved the List in ClickUp after setting up the integration, the plugin may still be pointing at the old List ID, which either no longer exists or has been reassigned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify your List ID:&lt;/strong&gt; In ClickUp, open the List you want tasks created in. Click the three dots menu on the List name and select "Copy Link." The URL contains the List ID. Compare this against what is stored in your plugin settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 3: ClickUp API Token vs OAuth - Scope Issues
&lt;/h2&gt;

&lt;p&gt;ClickUp has two ways to authenticate:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Personal API Token:&lt;/strong&gt; Found in ClickUp under Settings → Apps → API Token. This token has full access to everything the account owner can access. Simple, works for personal use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OAuth App Token:&lt;/strong&gt; Generated through a ClickUp app registration. Requires selecting specific scopes (task:write, list:read, etc.) when the app is created.&lt;/p&gt;

&lt;p&gt;Most CF7 integration plugins use the personal API token. If your plugin uses OAuth (either its own OAuth app or a third-party service), the token may not have the &lt;code&gt;task:write&lt;/code&gt; scope enabled. The API call authenticates successfully but ClickUp rejects the task creation because the token lacks permission to write tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to confirm:&lt;/strong&gt; Use the personal API token approach wherever your plugin offers a choice. Test it directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"https://api.clickup.com/api/v2/user"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: YOUR_PERSONAL_API_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see your user details, the token is valid and has basic access. Then test task creation specifically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.clickup.com/api/v2/list/YOUR_LIST_ID/task"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: YOUR_PERSONAL_API_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "name": "Test Task from CF7",
    "description": "This is a test"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 200 response with a task object confirms your token, List ID, and permissions are all correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 4: Custom Fields Locked Behind a Pro Paywall
&lt;/h2&gt;

&lt;p&gt;If your integration is creating tasks in ClickUp but the custom field values are not being populated, check whether your plugin requires a Pro upgrade for custom field support.&lt;/p&gt;

&lt;p&gt;The Advanced Form Integration plugin's ClickUp documentation states explicitly: "Requires a Pro license to add tags and custom fields." Free plan users can create tasks with a name and description, but mapping CF7 form fields to ClickUp custom fields requires the paid version.&lt;/p&gt;

&lt;p&gt;This produces a confusing situation: the integration appears to work (tasks are created), but most of the form data never makes it into ClickUp because it was mapped to custom fields that the free plugin version does not send.&lt;/p&gt;

&lt;p&gt;If this is your situation, either upgrade the plugin or switch to a direct API integration where you control exactly which custom fields receive which values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting CF7 to ClickUp Directly
&lt;/h2&gt;

&lt;p&gt;ClickUp's API for creating a task with custom fields:&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;POST https://api.clickup.com/api/v2/list/{list_id}/task
Authorization: YOUR_PERSONAL_API_TOKEN
Content-Type: application/json

{
  "name": "Enquiry from Jane Smith",
  "description": "Email: jane@example.com\nMessage: Their message here",
  "custom_fields": [
    {
      "id": "CUSTOM_FIELD_ID",
      "value": "jane@example.com"
    }
  ]
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get your custom field IDs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.clickup.com/api/v2/list/YOUR_LIST_ID/field"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: YOUR_PERSONAL_API_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each field object has an &lt;code&gt;id&lt;/code&gt; property — that is what you pass in &lt;code&gt;custom_fields[].id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.contactformtoapi.com/contact-form-7-third-party-integration-guide/" rel="noopener noreferrer"&gt;Contact Form to API&lt;/a&gt; handles this direct API call from the WordPress dashboard. You set the ClickUp task creation endpoint, add your personal API token as the Authorization header, and define the JSON body mapping your CF7 fields to ClickUp's expected structure. Custom fields, tags, assignees all controlled from your settings without a Pro paywall gate on basic field mapping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Diagnosis
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Most Likely Cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;FIELD_010&lt;/code&gt; error in logs&lt;/td&gt;
&lt;td&gt;CF7 field type does not match ClickUp custom field type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tasks created but no custom field data&lt;/td&gt;
&lt;td&gt;Plugin free tier does not send custom fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No tasks created, no error&lt;/td&gt;
&lt;td&gt;Wrong List selected (Space or Folder chosen instead)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tasks created pointing to old List&lt;/td&gt;
&lt;td&gt;List was renamed/moved after setup — List ID is stale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;401 authentication error&lt;/td&gt;
&lt;td&gt;API token invalid or OAuth scopes insufficient&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>wordpress</category>
      <category>clickup</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CF7 to Slack Notifications Not Working - What Is Actually Broken</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Tue, 08 Sep 2026 07:31:45 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-slack-notifications-not-working-what-is-actually-broken-2ei0</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-slack-notifications-not-working-what-is-actually-broken-2ei0</guid>
      <description>&lt;p&gt;Your team set up CF7 to send a Slack message every time someone fills in the contact form. It worked for a while. Then one day the notifications stopped. Or maybe you are setting it up fresh and the Slack channel is completely silent even after the form submits successfully.&lt;/p&gt;

&lt;p&gt;There are four reasons this happens. Three of them have nothing to do with your CF7 configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 1: You Are Using a Legacy Slack Webhook URL
&lt;/h2&gt;

&lt;p&gt;Slack has two types of incoming webhooks and they are not interchangeable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Legacy Incoming Webhooks&lt;/strong&gt; were created before Slack apps existed. They look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;App-based Incoming Webhooks&lt;/strong&gt; are the current supported method. They look identical in format but are generated through a Slack App in your workspace.&lt;/p&gt;

&lt;p&gt;Slack stopped creating new legacy webhooks years ago and has been progressively deprecating them. If your webhook URL was generated more than a few years ago through the old "Incoming Webhooks" configuration in Slack settings (not through an app), it may have stopped working without any notification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to confirm:&lt;/strong&gt; Send a test POST to your webhook URL directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"text":"Test from WordPress"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  YOUR_WEBHOOK_URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you get &lt;code&gt;invalid_token&lt;/code&gt; or &lt;code&gt;no_service&lt;/code&gt; in the response, the webhook is retired. If you get &lt;code&gt;ok&lt;/code&gt;, the webhook is still active.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Create a fresh Slack App and generate a new webhook:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;code&gt;api.slack.com/apps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click "Create New App" → "From scratch"&lt;/li&gt;
&lt;li&gt;Name it something like "WordPress CF7 Alerts"&lt;/li&gt;
&lt;li&gt;Under "Add features and functionality", choose "Incoming Webhooks"&lt;/li&gt;
&lt;li&gt;Toggle "Activate Incoming Webhooks" to On&lt;/li&gt;
&lt;li&gt;Click "Add New Webhook to Workspace"&lt;/li&gt;
&lt;li&gt;Choose the channel and click Allow&lt;/li&gt;
&lt;li&gt;Copy the new webhook URL&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Update this URL in your CF7 Slack plugin settings or in your custom code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 2: The Dedicated Plugin Is Abandoned
&lt;/h2&gt;

&lt;p&gt;Most CF7 to Slack plugins on WordPress.org were last updated years ago. The "Slack Contact Form 7" plugin's most recent support thread reported it not working in WordPress 5.8 — with zero replies from the plugin author. That was in 2021. The plugin has not been updated since.&lt;/p&gt;

&lt;p&gt;An abandoned plugin that hooks into CF7's submission process will break silently when CF7 or WordPress updates change the internal hooks the plugin relies on. The form submits. The plugin fires. Something in the hook chain fails. No Slack message arrives. No error appears anywhere.&lt;/p&gt;

&lt;p&gt;If you are using a dedicated CF7 Slack plugin, check its last updated date in your WordPress admin. If it has not been updated in the past year, it is almost certainly running against outdated CF7 internals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 3: The Webhook URL Is Correct But the Payload Format Is Wrong
&lt;/h2&gt;

&lt;p&gt;Slack's Incoming Webhooks API has evolved. The original format sent a simple &lt;code&gt;text&lt;/code&gt; field:&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New form submission from Jane Smith"&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;The current recommended format uses Block Kit:&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;"blocks"&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;"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;"section"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"text"&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;"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;"mrkdwn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*New CF7 Submission*&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;*Name:* Jane Smith&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;*Email:* jane@example.com"&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;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;Old plugins that send the legacy text format still work for now but may produce warnings or stop working if Slack deprecates the simple text format fully. More importantly, if a plugin is sending a malformed payload, Slack returns an error that the plugin silently discards.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Connect CF7 to Slack Without a Dedicated Plugin
&lt;/h2&gt;

&lt;p&gt;The most reliable approach is to call Slack's webhook directly from CF7 using &lt;a href="https://www.contactformtoapi.com/automate-wordpress-form-submissions/" rel="noopener noreferrer"&gt;Contact Form to API&lt;/a&gt;. You configure the Slack webhook URL as the API endpoint, set &lt;code&gt;Content-Type: application/json&lt;/code&gt; as the header, and define the message payload using your CF7 field values.&lt;/p&gt;

&lt;p&gt;A simple payload that puts the form data in a readable Slack message:&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;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New contact form submission:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;*Name:* [your-name]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;*Email:* [your-email]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;*Message:* [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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with Block Kit for a cleaner Slack message:&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;"blocks"&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;"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;"header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"text"&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;"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;"plain_text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Website Enquiry"&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;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;"section"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fields"&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="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;"mrkdwn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*Name:*&lt;/span&gt;&lt;span class="se"&gt;\n&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="p"&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;"mrkdwn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*Email:*&lt;/span&gt;&lt;span class="se"&gt;\n&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="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;"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;"section"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"text"&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;"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;"mrkdwn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*Message:*&lt;/span&gt;&lt;span class="se"&gt;\n&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;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;Replace &lt;code&gt;[your-name]&lt;/code&gt;, &lt;code&gt;[your-email]&lt;/code&gt;, and &lt;code&gt;[your-message]&lt;/code&gt; with your actual CF7 field names. Contact Form to API substitutes the real submitted values before sending the request to Slack.&lt;/p&gt;

&lt;p&gt;No dedicated Slack plugin needed. No abandoned codebase to worry about. When Slack updates their webhook format, you update the payload in your settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 4: Slack Rate Limiting or App Permissions
&lt;/h2&gt;

&lt;p&gt;If notifications were working and then stopped intermittently, Slack may be rate limiting your webhook. Slack allows 1 message per second per webhook URL. If your form receives bursts of submissions, some Slack messages may be dropped with a &lt;code&gt;429 Too Many Requests&lt;/code&gt; response that the plugin never surfaces.&lt;/p&gt;

&lt;p&gt;Also check whether the Slack app that owns your webhook is still installed in your workspace. Workspace admins can remove apps, which immediately invalidates all webhooks created by that app.&lt;/p&gt;

&lt;p&gt;In Slack: go to your workspace settings, then Apps, and confirm your WordPress integration app is still listed and active.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Diagnosis
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Test your webhook URL directly&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"text":"CF7 webhook test"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://hooks.slack.com/services/YOUR/WEBHOOK/URL

&lt;span class="c"&gt;# Expected: ok&lt;/span&gt;
&lt;span class="c"&gt;# invalid_token or no_service = webhook retired, create a new one&lt;/span&gt;
&lt;span class="c"&gt;# channel_not_found = the channel was deleted or renamed&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: Check the channel still exists in Slack&lt;/span&gt;
&lt;span class="c"&gt;# The channel the webhook was configured for must still exist&lt;/span&gt;

&lt;span class="c"&gt;# Step 3: Confirm the plugin's last update date&lt;/span&gt;
&lt;span class="c"&gt;# WordPress admin → Plugins → check "Last Updated" for your Slack plugin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>wordpress</category>
      <category>slack</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CF7 to Notion Broke on August 11, 2026 - Here Is Exactly What Happened</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Mon, 07 Sep 2026 09:48:53 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-notion-broke-on-august-11-2026-here-is-exactly-what-happened-1ei7</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-notion-broke-on-august-11-2026-here-is-exactly-what-happened-1ei7</guid>
      <description>&lt;p&gt;On August 11, 2026, Notion started strictly enforcing the &lt;code&gt;Bearer&lt;/code&gt; prefix in Authorization headers. CF7-to-Notion plugin integrations that were working that morning were broken by that afternoon. Forms started showing errors to visitors. The Notion database list disappeared from the plugin settings. Every API call returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;401 unauthorized: API token is invalid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token was not invalid. The integration was not misconfigured. Notion simply changed how strictly it validates the Authorization header format - and the plugin was sending the token without the required &lt;code&gt;Bearer&lt;/code&gt; prefix.&lt;/p&gt;

&lt;p&gt;This is one of the cleanest and most thoroughly documented bugs I have seen in the WordPress support forums. The developer who filed the report identified the exact line of code, proved the cause with a command-line test, wrote a working workaround, and even flagged a PHP 9 breaking change in the same plugin. The plugin team responded within days with a patch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Root Cause: Missing &lt;code&gt;Bearer&lt;/code&gt; Prefix in the Authorization Header
&lt;/h2&gt;

&lt;p&gt;HTTP Bearer authentication requires this exact header format:&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 secret_abc123...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CF7 Notion addon (version 1.6.2 and earlier) was sending:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authorization: secret_abc123...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The raw token, no prefix. This is the relevant line from the plugin source at &lt;code&gt;includes/classes/class-api-notion.php&lt;/code&gt;:&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="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&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;secret_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Until August 11, Notion's API accepted both formats. After that date, Notion tightened its header parsing and started rejecting the bare token with a 401. The plugin code had not changed. Notion's validation had.&lt;/p&gt;

&lt;p&gt;The developer confirmed this with a direct command-line test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# With correct Bearer prefix — returns 200 OK&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer secret_..."&lt;/span&gt; https://api.notion.com/v1/databases

&lt;span class="c"&gt;# Without Bearer prefix — returns 401&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: secret_..."&lt;/span&gt; https://api.notion.com/v1/databases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same token. Same endpoint. Different header format. Different result.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You Are on Plugin Version 1.6.2 or Earlier
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Update to version 1.6.3 immediately.&lt;/strong&gt; The plugin team released the fix on August 24, 2026. No settings changes are needed after updating. The plugin adds the &lt;code&gt;Bearer&lt;/code&gt; prefix and also includes a guard for tokens that had the prefix manually added via the workaround filter.&lt;/p&gt;

&lt;p&gt;If your automatic updates are delayed or you cannot update right now, apply this temporary fix using the WPCode or Code Snippets plugin:&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;'add-on-cf7-for-notion/notion-api/request-args'&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;$args&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="k"&gt;empty&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="s1"&gt;'headers'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nb"&gt;stripos&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="s1"&gt;'headers'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&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="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'headers'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'headers'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&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;$args&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;Add this as a PHP snippet, activate it, then go to Contact in your WordPress admin and reload the Integration tab. Your Notion databases should reappear immediately.&lt;/p&gt;

&lt;p&gt;Once you update to 1.6.3 you can safely remove this filter - the version 1.6.3 patch handles the &lt;code&gt;Bearer&lt;/code&gt; prefix natively.&lt;/p&gt;

&lt;h2&gt;
  
  
  The PHP 9 Breaking Change Also In This Plugin
&lt;/h2&gt;

&lt;p&gt;The same developer identified a second bug in the same code. The &lt;code&gt;get_databases()&lt;/code&gt; pagination function contains:&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="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sleep()&lt;/code&gt; takes an integer argument. Passing &lt;code&gt;0.25&lt;/code&gt; (a float) silently becomes &lt;code&gt;0&lt;/code&gt; in PHP 8 - the intended quarter-second throttle never happens. On PHP 8.1+, this logs a deprecation notice:&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="no"&gt;PHP&lt;/span&gt; &lt;span class="nc"&gt;Deprecated&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Implicit&lt;/span&gt; &lt;span class="n"&gt;conversion&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;float&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="n"&gt;loses&lt;/span&gt; &lt;span class="n"&gt;precision&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On PHP 9, this becomes a &lt;code&gt;TypeError&lt;/code&gt; - a fatal error that will break database listing entirely. The correct call is:&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="nb"&gt;usleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;250000&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 250,000 microseconds = 0.25 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin team included this fix in version 1.6.3 as well. If you are running PHP 8.1+ you may already see the deprecation notice in your error logs. Updating to 1.6.3 fixes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Lesson: External API Enforcement Changes Break Integrations Silently
&lt;/h2&gt;

&lt;p&gt;This incident is a precise example of a category of failure that is extremely common but almost never anticipated: an external service changes how strictly it validates its API without breaking changes to the response format, and plugins that were previously working break overnight.&lt;/p&gt;

&lt;p&gt;Notion did not change its API version. They did not remove an endpoint. They did not change the response format. They just started enforcing an existing requirement more strictly. The plugin's requests had always been technically malformed — they just happened to work until Notion decided to start caring.&lt;/p&gt;

&lt;p&gt;This happens with auth headers, content-type headers, API version headers, and rate limiting. The plugin that worked for two years can fail on a Tuesday morning because the receiving service tightened validation on Monday evening.&lt;/p&gt;

&lt;p&gt;The only way to know when this happens is to have response logging on every API call. &lt;a href="https://www.contactformtoapi.com/contact-form-7-third-party-integration-guide/" rel="noopener noreferrer"&gt;Contact Form to API&lt;/a&gt; logs the response from every submission attempt. When Notion started returning 401s, you would have seen it in the log on the first failed submission rather than finding out when a client called to say their form was broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting CF7 to Notion Directly
&lt;/h2&gt;

&lt;p&gt;For a more resilient CF7 to Notion integration, you can call Notion's API directly rather than relying on a dedicated plugin.&lt;/p&gt;

&lt;p&gt;Notion's API for creating a page (database row):&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;POST https://api.notion.com/v1/pages
Authorization: Bearer YOUR_NOTION_TOKEN
Content-Type: application/json
Notion-Version: 2022-06-28

{
  "parent": { "database_id": "YOUR_DATABASE_ID" },
  "properties": {
    "Name": {
      "title": [{ "text": { "content": "Jane Smith" } }]
    },
    "Email": {
      "email": "jane@example.com"
    },
    "Phone": {
      "phone_number": "1234567890"
    }
  }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that Notion requires the &lt;code&gt;Notion-Version&lt;/code&gt; header (&lt;code&gt;2022-06-28&lt;/code&gt; is the current stable version). Property names in the payload must exactly match the column names in your Notion database. Property types (&lt;code&gt;title&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;phone_number&lt;/code&gt;, &lt;code&gt;rich_text&lt;/code&gt;, etc.) must match the column type configured in Notion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Cause&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;401 on all Notion API calls from August 11, 2026&lt;/td&gt;
&lt;td&gt;Plugin sending token without &lt;code&gt;Bearer&lt;/code&gt; prefix&lt;/td&gt;
&lt;td&gt;Update to v1.6.3 or apply filter workaround&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deprecation notices for &lt;code&gt;sleep(0.25)&lt;/code&gt; in PHP logs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sleep()&lt;/code&gt; called with float argument&lt;/td&gt;
&lt;td&gt;Update to v1.6.3 (uses &lt;code&gt;usleep(250000)&lt;/code&gt; instead)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Will break on PHP 9&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sleep(0.25)&lt;/code&gt; becomes &lt;code&gt;TypeError&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Update to v1.6.3 now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notion databases not appearing in plugin after token entry&lt;/td&gt;
&lt;td&gt;Same 401 issue&lt;/td&gt;
&lt;td&gt;Same fix as above&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>wordpress</category>
      <category>api</category>
      <category>notion</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CF7 REST API Returns `wpcf7_forbidden` 403 - Why It Happens and How to Fix It</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Thu, 03 Sep 2026 07:05:58 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-rest-api-returns-wpcf7forbidden-403-why-it-happens-and-how-to-fix-it-284a</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-rest-api-returns-wpcf7forbidden-403-why-it-happens-and-how-to-fix-it-284a</guid>
      <description>&lt;p&gt;If you are building a headless WordPress site with React or Next.js and calling CF7's REST API, you have probably hit 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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wpcf7_forbidden"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are not allowed to access the requested contact form."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;403&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;The confusing part: you are logged in as an admin. You can see the form in the WordPress backend. But the API tells you that you are forbidden.&lt;/p&gt;

&lt;p&gt;This happens because &lt;code&gt;wpcf7_forbidden&lt;/code&gt; covers two completely different situations that need completely different fixes. Getting them mixed up is why most debugging attempts fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Two Endpoints Are Not the Same
&lt;/h2&gt;

&lt;p&gt;CF7 has two separate REST API endpoints and they have different authentication requirements:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GET endpoint — reads form configuration:&lt;/strong&gt;&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;GET /wp-json/contact-form-7/v1/contact-forms/{id}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the form's HTML, fields, and schema. It requires the user to have the &lt;code&gt;wpcf7_edit_contact_form&lt;/code&gt; capability, which means they must be logged in as an administrator or editor. Unauthenticated requests always get &lt;code&gt;wpcf7_forbidden&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;POST endpoint — submits the form:&lt;/strong&gt;&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;POST /wp-json/contact-form-7/v1/contact-forms/{id}/feedback
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This processes a form submission. It does &lt;strong&gt;not&lt;/strong&gt; require authentication. Any visitor can submit a form. But it does require specific hidden fields in the payload.&lt;/p&gt;

&lt;p&gt;Most &lt;code&gt;wpcf7_forbidden&lt;/code&gt; errors on the feedback endpoint are not authentication problems. They are missing field problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 1: Calling the GET Endpoint Without Authentication
&lt;/h2&gt;

&lt;p&gt;A developer posted on the WordPress forums that their headless React app was getting &lt;code&gt;wpcf7_forbidden&lt;/code&gt; when fetching form data. They were calling:&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;GET /wp-json/contact-form-7/v1/contact-forms/21798
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CF7 source code for this endpoint's permission callback is:&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="s1"&gt;'permission_callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;WP_REST_Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&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="nf"&gt;current_user_can&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wpcf7_edit_contact_form'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WP_Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'wpcf7_forbidden'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'You are not allowed to access the requested contact form.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'contact-form-7'&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;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;403&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;This capability check (&lt;code&gt;wpcf7_edit_contact_form&lt;/code&gt;) returns false for unauthenticated requests. There is no way around this without authentication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Do not call the GET endpoint from client-side code without authentication. Instead, fetch the form HTML server-side in Next.js using your WordPress credentials, then render it client-side. Or hardcode the form HTML from WordPress admin and skip the GET endpoint entirely.&lt;/p&gt;

&lt;p&gt;In Next.js, fetch the form server-side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In a Next.js Server Component or getServerSideProps&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WP_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/wp-json/contact-form-7/v1/contact-forms/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;formId&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Basic &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WP_USER&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WP_APP_PASSWORD&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="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&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;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use WordPress Application Passwords (generated under Users, Profile, Application Passwords) rather than your account password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 2: Missing Required Hidden Fields on the Feedback Endpoint
&lt;/h2&gt;

&lt;p&gt;When the &lt;code&gt;wpcf7_forbidden&lt;/code&gt; error appears on the POST feedback endpoint during form submission (not the GET endpoint), the cause is almost always missing hidden fields in the payload.&lt;/p&gt;

&lt;p&gt;CF7's feedback endpoint requires these fields alongside your form data:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Where to get it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_wpcf7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Form ID (integer)&lt;/td&gt;
&lt;td&gt;From the form's edit URL in WordPress admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_wpcf7_version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CF7 version&lt;/td&gt;
&lt;td&gt;From the rendered form HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_wpcf7_locale&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Site locale&lt;/td&gt;
&lt;td&gt;Usually &lt;code&gt;en_US&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_wpcf7_unit_tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique form tag&lt;/td&gt;
&lt;td&gt;From the rendered form HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_wpcf7_container_post&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Post ID where form is embedded&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; if not embedded in a post&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A minimal correct payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_wpcf7&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;               &lt;span class="c1"&gt;// your form ID&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_wpcf7_version&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5.9.8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// CF7 version&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_wpcf7_locale&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en_US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_wpcf7_unit_tag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wpcf7-f123-p456-o1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_wpcf7_container_post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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-name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jane Smith&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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-email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jane@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://your-wp-site.com/wp-json/contact-form-7/v1/contact-forms/123/feedback`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&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;The &lt;code&gt;_wpcf7_unit_tag&lt;/code&gt; value is generated by CF7 when it renders the form. In a headless setup, you can either fetch it from the GET endpoint server-side or construct it using the pattern &lt;code&gt;wpcf7-f{formId}-p{postId}-o{instance}&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 3: Nginx Redirect Misconfiguration
&lt;/h2&gt;

&lt;p&gt;One developer reported &lt;code&gt;wpcf7_forbidden&lt;/code&gt; appearing on all their forms suddenly with no other changes. After debugging, they found it was caused by a redirect rule in their Nginx configuration that was rewriting the request URL before it reached WordPress.&lt;/p&gt;

&lt;p&gt;The Nginx rule was modifying the path in a way that stripped or altered the form ID segment of the URL. WordPress received a modified URL that did not match the route pattern, fired the permission callback with incorrect parameters, and returned &lt;code&gt;wpcf7_forbidden&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If your &lt;code&gt;wpcf7_forbidden&lt;/code&gt; error appeared suddenly without any code changes, check your Nginx configuration for any recently added location blocks or rewrite rules that might affect &lt;code&gt;/wp-json/&lt;/code&gt; paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Headless CF7 Integration Pattern That Avoids All of This
&lt;/h2&gt;

&lt;p&gt;If you are building a headless WordPress site and your goal is to collect form data and send it somewhere useful (a CRM, an email service, a Slack channel), there is a simpler architecture that avoids the REST API authentication complexity entirely.&lt;/p&gt;

&lt;p&gt;Keep CF7 on the WordPress side. Use &lt;a href="https://www.contactformtoapi.com/contact-form-7-third-party-integration-guide/" rel="noopener noreferrer"&gt;Contact Form to API&lt;/a&gt; to forward form submissions to your external service via the server-side &lt;code&gt;wpcf7_before_send_mail&lt;/code&gt; hook. Your Next.js or React frontend embeds the WordPress CF7 form directly (via an iframe or a fetched HTML block), the form submits to WordPress normally, and WordPress forwards the data to your CRM.&lt;/p&gt;

&lt;p&gt;No GET endpoint authentication. No hidden field management. No CORS configuration. The form submission stays within the WordPress ecosystem and the outbound CRM call happens server-to-server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Error cause&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;Calling GET endpoint without auth&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;wpcf7_edit_contact_form&lt;/code&gt; capability check fails&lt;/td&gt;
&lt;td&gt;Use Application Password auth server-side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST feedback with missing fields&lt;/td&gt;
&lt;td&gt;Required hidden fields absent&lt;/td&gt;
&lt;td&gt;Include all &lt;code&gt;_wpcf7_*&lt;/code&gt; hidden fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sudden 403 on all forms, no code change&lt;/td&gt;
&lt;td&gt;Nginx redirect modifying URL&lt;/td&gt;
&lt;td&gt;Check Nginx config for recently added rewrite rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intermittent 403 on form submission&lt;/td&gt;
&lt;td&gt;Nonce expired between page load and submit&lt;/td&gt;
&lt;td&gt;CF7 handles nonces internally — usually not the cause on standard submissions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>wordpress</category>
      <category>javascript</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CF7 REST API Returns `rest_no_route` 404 Every Cause Explained</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Wed, 02 Sep 2026 10:28:12 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-rest-api-returns-restnoroute-404-every-cause-explained-53nd</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-rest-api-returns-restnoroute-404-every-cause-explained-53nd</guid>
      <description>&lt;p&gt;The error looks the same every time:&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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rest_no_route"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No route was found matching the URL and request method."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;404&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;But the causes are completely different depending on your setup. Across multiple WordPress support threads, developers have hit this error from four separate root causes that look identical in the browser console. This post covers all four so you can identify and fix yours in one pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;rest_no_route&lt;/code&gt; Actually Means
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;rest_no_route&lt;/code&gt; is WordPress telling you it received an HTTP request to a URL that does not match any registered REST API route. It is a routing failure, not an authentication failure, not a plugin failure, and not a CF7 failure.&lt;/p&gt;

&lt;p&gt;WordPress's REST API router receives the request and looks through its registered routes for a match. When nothing matches, it returns this 404. The cause is always something that prevents the URL from reaching the correct route handler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 1: Wrong Form ID in the URL
&lt;/h2&gt;

&lt;p&gt;This was the resolution in the most-viewed thread on the topic. A developer was testing the feedback endpoint in Postman using this URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/wp-json/contact-form-7/v1/contact-forms/fc7114d/feedback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CF7 plugin author's reply was direct: &lt;code&gt;fc7114d&lt;/code&gt; is not a contact form ID. CF7 form IDs are integers, not alphanumeric strings.&lt;/p&gt;

&lt;p&gt;The correct URL format is:&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;POST /wp-json/contact-form-7/v1/contact-forms/123/feedback
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;123&lt;/code&gt; is the integer ID of your specific CF7 form. Find it in the WordPress admin under Contact, Contact Forms. The form ID appears in the URL when you open the form for editing: &lt;code&gt;post=123&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you are following a tutorial or building a headless Next.js integration, make sure the form ID you are using is the actual integer ID of a real form on your site, not a placeholder or slug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 2: WP Rocket Caching the REST API Response
&lt;/h2&gt;

&lt;p&gt;This was the cause in two separate threads. A developer reported that CF7 forms stopped submitting suddenly with the &lt;code&gt;rest_no_route&lt;/code&gt; error. When they disabled WP Rocket, the forms worked immediately. Re-enabling WP Rocket brought the error back.&lt;/p&gt;

&lt;p&gt;WP Rocket can cache REST API responses under certain configurations. When a cached 404 response for the feedback URL is served instead of the live WordPress router handling the request, &lt;code&gt;rest_no_route&lt;/code&gt; appears even though the route exists and is correctly registered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; In WP Rocket settings, go to Advanced Rules and add the CF7 feedback URL pattern to the "Never Cache URLs" list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/wp-json/contact-form-7/(.*)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also check WP Rocket's CDN settings if you use one. Some CDN configurations cache API responses at the edge level, which requires an edge rule to bypass caching for &lt;code&gt;/wp-json/&lt;/code&gt; paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 3: &lt;code&gt;.htaccess&lt;/code&gt; or Nginx Misconfiguration
&lt;/h2&gt;

&lt;p&gt;A plugin author response in one thread identified this directly: the &lt;code&gt;rest_no_route&lt;/code&gt; error can be caused by Apache mod_rewrite rules or an Nginx server block that is not correctly passing requests to WordPress's REST API handler.&lt;/p&gt;

&lt;p&gt;For Nginx servers, the REST API requires this location block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.php?&lt;/span&gt;&lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;/wp-json/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;rewrite&lt;/span&gt; &lt;span class="s"&gt;^/wp-json/(.*)&lt;/span&gt;$ &lt;span class="n"&gt;/index.php?rest_route=/&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="s"&gt;last&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;Without the second block, Nginx does not know how to route &lt;code&gt;/wp-json/&lt;/code&gt; requests to WordPress's PHP handler. Requests fall through to a 404 at the server level before WordPress even sees them.&lt;/p&gt;

&lt;p&gt;For Apache, re-saving your WordPress permalinks usually regenerates the &lt;code&gt;.htaccess&lt;/code&gt; file with the correct rewrite rules. Go to Settings, then Permalinks, and click Save Changes without changing anything.&lt;/p&gt;

&lt;p&gt;One developer in the forums reported that re-saving permalinks temporarily fixed the issue but it kept returning. This is a sign of a plugin or a deployment process overwriting the &lt;code&gt;.htaccess&lt;/code&gt; file. Check whether any deployment script or security plugin regenerates &lt;code&gt;.htaccess&lt;/code&gt; without the correct WordPress rewrite rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 4: HTTP to HTTPS Redirect Intercepting the API Request
&lt;/h2&gt;

&lt;p&gt;One thread showed a CORS error alongside the &lt;code&gt;rest_no_route&lt;/code&gt; response. The actual issue was that the site had references to the HTTP version of the domain (&lt;code&gt;http://&lt;/code&gt;) somewhere in its configuration. When CF7's JavaScript fired the feedback request over HTTPS, a redirect from HTTP to HTTPS was intercepting it and the redirect destination was serving a 404.&lt;/p&gt;

&lt;p&gt;Check your browser's Network tab when submitting a form. If you see a redirect (301 or 302) happening before the 404, the request is being redirected somewhere it should not be. Confirm that all references to your domain in WordPress settings use HTTPS, not HTTP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WordPress Address (URL): https://yourdomain.com
Site Address (URL): https://yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If these are set to HTTP, update them under Settings, General, and then clear any caches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirming the Route Is Actually Registered
&lt;/h2&gt;

&lt;p&gt;Before debugging any of the above, confirm that CF7's REST API route exists on your site by visiting:&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.com/wp-json/contact-form-7/v1/contact-forms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see a JSON array of your forms, the route is registered correctly and the problem is in the feedback submission specifically. If you see &lt;code&gt;rest_no_route&lt;/code&gt; even at this URL, CF7 itself may not be active or there is a server-level routing problem blocking all &lt;code&gt;/wp-json/&lt;/code&gt; requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Different Architecture: Skip the REST API Dependency
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;rest_no_route&lt;/code&gt; error exists because CF7's form submission uses the REST API by default. Every caching layer, server configuration, and security plugin that touches &lt;code&gt;/wp-json/&lt;/code&gt; is a potential point of failure.&lt;/p&gt;

&lt;p&gt;If your goal is to send CF7 form data to an external CRM or API, &lt;a href="https://www.contactformtoapi.com/automate-wordpress-form-submissions/" rel="noopener noreferrer"&gt;Contact Form to API&lt;/a&gt; handles the outbound API call from the server side on &lt;code&gt;wpcf7_before_send_mail&lt;/code&gt;. The form submission goes through CF7's normal processing path, not through the REST API endpoint. There is no &lt;code&gt;/wp-json/feedback&lt;/code&gt; URL for caching plugins to intercept or for server routing misconfigurations to break.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Diagnosis Checklist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Most Likely Cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Error in Postman with alphanumeric form ID&lt;/td&gt;
&lt;td&gt;Wrong form ID format — use integer ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works with WP Rocket disabled&lt;/td&gt;
&lt;td&gt;WP Rocket caching &lt;code&gt;/wp-json/&lt;/code&gt; — add never-cache rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intermittent error, re-saving permalinks fixes temporarily&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.htaccess&lt;/code&gt; being overwritten&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redirect visible in Network tab before the 404&lt;/td&gt;
&lt;td&gt;HTTP/HTTPS mismatch in site URL settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;rest_no_route&lt;/code&gt; even at &lt;code&gt;/wp-json/contact-form-7/v1/contact-forms&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;CF7 not active or server blocking all REST routes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>wordpress</category>
      <category>api</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CF7 to GetResponse Not Adding Contacts - What Is Actually Wrong</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Tue, 01 Sep 2026 10:35:00 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-getresponse-not-adding-contacts-what-is-actually-wrong-1cb</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-to-getresponse-not-adding-contacts-what-is-actually-wrong-1cb</guid>
      <description>&lt;p&gt;A reviewer left this on the WordPress plugin page for the CF7 GetResponse Extension:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The UI is great on the backend but the plugin is not working and there are no logs for me to see why contacts are not being added. Please fix."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another review from earlier said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Not working. Display error 'Invalid key' even key is valid."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both describe the same underlying situation: the integration appears set up correctly, the form submits without error, and GetResponse receives nothing. No error message. No log entry. Just silence.&lt;/p&gt;

&lt;p&gt;There are three reasons this happens with CF7 to GetResponse integrations, and none of them have anything to do with your API key being wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 1: GetResponse Deprecated Their API - The Plugin Still Uses the Old Endpoint
&lt;/h2&gt;

&lt;p&gt;GetResponse updated their API from v2 to v3 several years ago and the v2 endpoints have been progressively deprecated. The CF7 GetResponse Extension plugin was built against the older API. If your API key is valid but the plugin keeps saying it is invalid, the request is reaching a deprecated or retired endpoint rather than GetResponse's current API.&lt;/p&gt;

&lt;p&gt;GetResponse's current API base URL 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.getresponse.com/v3/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Older integrations may be hitting:&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.getresponse.com/v2/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requests to v2 endpoints either return an error or are silently rejected depending on when GetResponse retired them. The "invalid key" error in this scenario is not about the key itself. It is about sending a valid key to an endpoint that no longer accepts it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 2: No Error Logging in the Plugin
&lt;/h2&gt;

&lt;p&gt;The reviewer's complaint about no logs is a real architectural problem. When the plugin fires an API call to GetResponse and receives an error response, it has nowhere to surface that error to you. The form shows success. GetResponse gets nothing. The plugin does not write to the WordPress error log. You have no signal that anything went wrong.&lt;/p&gt;

&lt;p&gt;To manually diagnose what GetResponse is actually receiving, enable WordPress debug logging temporarily:&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="c1"&gt;// Add to wp-config.php&lt;/span&gt;
&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_DEBUG'&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="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_DEBUG_LOG'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then test the GetResponse API directly with your key to confirm it works independently of WordPress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"https://api.getresponse.com/v3/campaigns"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Auth-Token: api-key YOUR_GETRESPONSE_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the header format: GetResponse API v3 uses &lt;code&gt;X-Auth-Token: api-key YOUR_KEY&lt;/code&gt;, not &lt;code&gt;Authorization: Bearer YOUR_KEY&lt;/code&gt;. If you see a list of campaigns in the response, your key is valid. If you see an authentication error, regenerate the key in GetResponse under Tools, API, API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 3: Field Shortcode Placement Bug
&lt;/h2&gt;

&lt;p&gt;One reviewer discovered that the plugin fails to detect form fields correctly when two CF7 shortcodes appear on the same line in the form template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[text* your-name placeholder "Name"] [email* your-email placeholder "Email"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin scans the form template to build the field mapping dropdown. When shortcodes share a line, the regex parsing misses the email field and it never appears in the mapper. You map your CF7 form to GetResponse but the email field is empty, so GetResponse rejects the contact creation silently because email is a required field.&lt;/p&gt;

&lt;p&gt;The fix is simple: put each CF7 shortcode on its own line in the form editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[text* your-name placeholder "Name"]
[email* your-email placeholder "Email"]
[submit "Send"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After saving the form with this layout, go back to the plugin settings and re-map the fields. The email field should now appear in the dropdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  The GetResponse API V3 Direct Integration
&lt;/h2&gt;

&lt;p&gt;If the dedicated plugin continues to fail after these fixes, calling GetResponse's API directly removes the dependency on the plugin's implementation staying current.&lt;/p&gt;

&lt;p&gt;GetResponse V3 API for adding a contact to a campaign:&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;POST https://api.getresponse.com/v3/contacts
X-Auth-Token: api-key YOUR_API_KEY
Content-Type: application/json

{
  "email": "jane@example.com",
  "name": "Jane Smith",
  "campaign": {
    "campaignId": "YOUR_CAMPAIGN_ID"
  }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get your campaign ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"https://api.getresponse.com/v3/campaigns"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Auth-Token: api-key YOUR_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;campaignId&lt;/code&gt; field appears in the response for each campaign.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.contactformtoapi.com/" rel="noopener noreferrer"&gt;Contact Form to API&lt;/a&gt; lets you configure this API call from the WordPress dashboard without PHP. You set the GetResponse endpoint, add the &lt;code&gt;X-Auth-Token&lt;/code&gt; header with your key, and map your CF7 fields to the JSON body. Every submission calls GetResponse's v3 API directly. You see the response in the logs so you know immediately whether the contact was added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Checklist
&lt;/h2&gt;

&lt;p&gt;If your CF7 to GetResponse integration is not adding contacts, go through these in order.&lt;/p&gt;

&lt;p&gt;Test your API key directly against the v3 campaigns endpoint to confirm it is valid.&lt;/p&gt;

&lt;p&gt;Check whether each CF7 shortcode is on its own line in the form template. If not, fix the line breaks and remap the fields.&lt;/p&gt;

&lt;p&gt;Enable WP_DEBUG_LOG and check for any error after a test submission.&lt;/p&gt;

&lt;p&gt;If the dedicated plugin is using an outdated API endpoint and you cannot update it, switch to a direct API call using Contact Form to API with GetResponse's v3 endpoint.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>emailmarketing</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CF7 + Zoho CRM: New Leads Overwriting Existing Records Instead of Creating New Ones</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Mon, 31 Aug 2026 12:03:22 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/cf7-zoho-crm-new-leads-overwriting-existing-records-instead-of-creating-new-ones-2pf5</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/cf7-zoho-crm-new-leads-overwriting-existing-records-instead-of-creating-new-ones-2pf5</guid>
      <description>&lt;p&gt;A site owner posted on the WordPress forums with a specific and frustrating problem. When someone submitted their CF7 form with the same name or similar data as an existing lead in Zoho CRM, the plugin was updating that existing record instead of creating a new one. Duplicate names were colliding. Real new leads were being lost by being merged into old records.&lt;/p&gt;

&lt;p&gt;The plugin support team's reply was two sentences. The free version does not support this. Buy the Pro version.&lt;/p&gt;

&lt;p&gt;Thread closed as resolved. The problem was not resolved for anyone who did not want to pay for a Pro upgrade just to control whether new leads get created or merged.&lt;/p&gt;

&lt;p&gt;This post explains why this happens, what the Zoho API actually does with duplicate records, and how to handle it correctly without being forced into a Pro upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Plugin Overwrites Records Instead of Creating New Ones
&lt;/h2&gt;

&lt;p&gt;Zoho CRM's API has two different endpoints for creating Lead records:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create (always new):&lt;/strong&gt;&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;POST https://www.zohoapis.com/crm/v2/Leads
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This always creates a brand new record regardless of whether a record with the same name or email already exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upsert (create or update):&lt;/strong&gt;&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;POST https://www.zohoapis.com/crm/v2/Leads/upsert
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks for a matching record first. If it finds one, it updates it. If it does not find one, it creates a new record.&lt;/p&gt;

&lt;p&gt;Most dedicated CF7 to Zoho plugins use the upsert endpoint by default because it avoids true duplicate records for the same person. But "same person" in Zoho's matching logic is determined by the duplicate check fields you configure in your Zoho account - typically email address.&lt;/p&gt;

&lt;p&gt;The problem in the forum thread was that the plugin was matching on name rather than email, or the Zoho account had duplicate check fields set to something other than email. When two leads had the same name or similar data, Zoho treated them as the same person and updated the existing record instead of creating a new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Zoho's Duplicate Check Actually Works
&lt;/h2&gt;

&lt;p&gt;In your Zoho CRM account, under Setup, then CRM Settings, then Modules and Fields, you can configure which fields are used to identify duplicate records. For Leads, the default is typically Email.&lt;/p&gt;

&lt;p&gt;If duplicate check is set to Email and two different people submit your form with different email addresses, Zoho will create two separate Lead records even if their names are identical. The email address is the unique identifier.&lt;/p&gt;

&lt;p&gt;If duplicate check is configured to include Last Name or Company, two leads with the same last name or company will collide and the second submission will update the first record.&lt;/p&gt;

&lt;p&gt;The forum user's problem was almost certainly in this Zoho-side configuration, not in the plugin itself. Checking and correcting the duplicate check fields in Zoho settings would have resolved it without needing the Pro version.&lt;/p&gt;

&lt;p&gt;To check this in Zoho: go to Setup, then Zoho CRM Settings, then Duplicate Check Preferences under the Leads module. Confirm that Email is the primary deduplication field and that Name is not included.&lt;/p&gt;

&lt;h2&gt;
  
  
  When You Genuinely Need Always-Create Behaviour
&lt;/h2&gt;

&lt;p&gt;Some businesses do need to always create a new Lead regardless of whether a matching record exists. For example, a business that tracks every enquiry separately even from returning contacts, or one where the same person might legitimately submit multiple distinct enquiries.&lt;/p&gt;

&lt;p&gt;For this use case, the correct API call is the basic create endpoint, not upsert. The free plugin defaulting to upsert is not wrong - it prevents genuine duplicates. But the option to override this and always create a new record should not require a paid upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controlling This With a Direct API Integration
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.contactformtoapi.com/contact-form-7-third-party-integration-guide/" rel="noopener noreferrer"&gt;Contact Form to API&lt;/a&gt; lets you configure exactly which Zoho endpoint receives your form data. You choose the create endpoint if you always want new records, or the upsert endpoint if you want deduplication. That choice lives in your settings, not behind a paywall.&lt;/p&gt;

&lt;p&gt;For always-create behaviour, configure the endpoint as:&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;POST https://www.zohoapis.com/crm/v2/Leads
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a payload 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;"data"&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;"Last_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="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;"Lead_Source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Web Form"&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;For upsert behaviour with email as the matching field, use:&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;POST https://www.zohoapis.com/crm/v2/Leads/upsert
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the same payload. Zoho will match on the duplicate check fields configured in your account settings.&lt;/p&gt;

&lt;p&gt;You also get a response log for every submission so you can see whether Zoho created a new record or updated an existing one, and the exact record ID returned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the Duplicate Check in Zoho First
&lt;/h2&gt;

&lt;p&gt;Before changing any plugin settings, go into your Zoho CRM account and confirm your duplicate check configuration. This is the most likely cause of the forum user's problem and it costs nothing to fix.&lt;/p&gt;

&lt;p&gt;In Zoho CRM: Setup, then CRM Settings, then Modules, then Leads, then Duplicate Check Preferences. Ensure Email is selected as the duplicate check field and that Last Name is not included unless you specifically want last name matching.&lt;/p&gt;

&lt;p&gt;If you want Zoho to always create new records regardless of any matching field, you can also disable the duplicate check entirely for the Leads module. New leads will always be created and no merging will happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Leads being overwritten instead of creating new records in Zoho CRM is almost always a duplicate check configuration issue, not a plugin bug. The fix is in Zoho's settings, not in buying a Pro plugin upgrade.&lt;/p&gt;

&lt;p&gt;For businesses that need explicit control over create vs upsert behaviour without depending on which tier of a dedicated plugin they are on, a direct API integration gives you that control from a configuration setting rather than a pricing tier.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>zoho</category>
      <category>crm</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Your Contact Form Created the Deal... Just in the Wrong Pipeline</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Fri, 07 Aug 2026 11:16:37 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/your-contact-form-created-the-deal-just-in-the-wrong-pipeline-2mb8</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/your-contact-form-created-the-deal-just-in-the-wrong-pipeline-2mb8</guid>
      <description>&lt;p&gt;One of the most interesting CRM bugs I've seen recently wasn't caused by a failed API request.&lt;/p&gt;

&lt;p&gt;In fact, the API worked perfectly.&lt;/p&gt;

&lt;p&gt;The website visitor filled out a Contact Form 7 form, the request reached Pipedrive successfully, and a new deal was created.&lt;/p&gt;

&lt;p&gt;Everything looked good...&lt;/p&gt;

&lt;p&gt;Until the sales team opened Pipedrive.&lt;/p&gt;

&lt;p&gt;The deal had been created in the &lt;strong&gt;wrong pipeline&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That meant automations didn't trigger, sales representatives never saw the enquiry in their normal workflow, and reporting immediately became inaccurate.&lt;/p&gt;

&lt;p&gt;Technically, nothing failed.&lt;/p&gt;

&lt;p&gt;From a business perspective, everything did.&lt;/p&gt;

&lt;p&gt;A discussion in the Pipedrive Community highlighted this exact type of issue. Developers expected website enquiries to enter a specific pipeline, but instead they were landing in the default one. It's a subtle problem, yet it's surprisingly common when building CRM integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP 200 doesn't mean "problem solved"
&lt;/h2&gt;

&lt;p&gt;Developers often celebrate this response:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```http id="dev01"&lt;br&gt;
HTTP/1.1 200 OK&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


And rightly so.

It confirms the API accepted the request.

But here's the catch:

A successful API response only tells you **the request was processed**.

It doesn't tell you whether the CRM created the record exactly the way your business expected.

There's a big difference between:



```text id="flow01"
✓ Deal Created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="flow02"&lt;br&gt;
✓ Deal Created&lt;br&gt;
✗ Wrong Pipeline&lt;br&gt;
✗ Wrong Stage&lt;br&gt;
✗ Wrong Workflow&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The API succeeded.

The implementation didn't.

## Your CRM workflow starts long before someone calls the customer

Sales pipelines exist for a reason.

They're more than visual boards.

Each pipeline usually has its own:

* automation rules
* notification workflows
* ownership logic
* reporting
* probability calculations
* sales stages

If a website enquiry lands in the wrong pipeline, all of those downstream processes are affected.

That's why pipeline mapping deserves as much attention as authentication or field validation.

## Contact Form 7 only collects information

One misconception I still hear is:

&amp;gt; "Contact Form 7 created the deal in the wrong pipeline."

Not exactly.

Contact Form 7 doesn't know anything about pipelines.

Its responsibility ends after collecting user input.

The workflow actually looks like this:



```text id="flow03"
Visitor
      │
      ▼
Contact Form 7
      │
      ▼
API Integration
      │
      ▼
Pipedrive
      │
      ▼
Pipeline Assignment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The pipeline isn't chosen by the form.&lt;/p&gt;

&lt;p&gt;It's determined by the API request.&lt;/p&gt;
&lt;h2&gt;
  
  
  The payload decides the destination
&lt;/h2&gt;

&lt;p&gt;Imagine two requests.&lt;/p&gt;

&lt;p&gt;The first one contains almost no business information.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```json id="json01"&lt;br&gt;
{&lt;br&gt;
  "title": "Website Enquiry"&lt;br&gt;
}&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The CRM creates a deal.

But because no pipeline information is supplied, it uses its default configuration.

Now compare that with a more complete request.



```json id="json02"
{
  "title": "Website Demo Request",
  "pipeline_id": 4,
  "stage_id": 2
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exactly the same API.&lt;/p&gt;

&lt;p&gt;Completely different outcome.&lt;/p&gt;

&lt;p&gt;That's why understanding the CRM's payload structure is just as important as understanding the API endpoint itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping should follow the business process
&lt;/h2&gt;

&lt;p&gt;Whenever I build CRM integrations, I start with a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where should this enquiry appear the moment it's created?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer changes everything.&lt;/p&gt;

&lt;p&gt;A contact form might create a deal in the Sales pipeline.&lt;/p&gt;

&lt;p&gt;A demo request could enter the Product Demo pipeline.&lt;/p&gt;

&lt;p&gt;A partnership enquiry may belong in a Business Development pipeline.&lt;/p&gt;

&lt;p&gt;Trying to send every form into the same workflow usually creates more manual work later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I check before deploying
&lt;/h2&gt;

&lt;p&gt;Before I consider any CRM integration complete, I verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the correct endpoint being used?&lt;/li&gt;
&lt;li&gt;Is the pipeline ID correct?&lt;/li&gt;
&lt;li&gt;Is the stage ID correct?&lt;/li&gt;
&lt;li&gt;Are custom fields mapped properly?&lt;/li&gt;
&lt;li&gt;Does the created record appear where the sales team expects?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only then do I know the integration is actually finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Contact Form to API helps
&lt;/h2&gt;

&lt;p&gt;This is where &lt;strong&gt;Contact Form to API&lt;/strong&gt; becomes useful.&lt;/p&gt;

&lt;p&gt;Instead of treating every Contact Form 7 submission the same way, it allows developers to build API requests that match the CRM's requirements.&lt;/p&gt;

&lt;p&gt;That includes configuring custom payloads, mapping fields, and sending requests to external APIs without being locked into a fixed workflow.&lt;/p&gt;

&lt;p&gt;If you're integrating Contact Form 7 with Pipedrive, these resources are worth checking out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easily Connect Contact Form 7 with Pipedrive&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/easily-connect-contact-form-7-with-pipedrive/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/easily-connect-contact-form-7-with-pipedrive/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contact Form 7 JSON Mapping&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/contact-form-7-json-mapping/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contact Form 7 CRM Integration Guide&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/contact-form-7-crm-integration/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/contact-form-7-crm-integration/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;One thing I've learned from building integrations is this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Moving data isn't the hard part.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Moving it into the &lt;strong&gt;right workflow&lt;/strong&gt; is.&lt;/p&gt;

&lt;p&gt;A deal sitting in the wrong pipeline can be just as damaging as a deal that never arrived at all.&lt;/p&gt;

&lt;p&gt;The next time you're testing a Contact Form 7 integration, don't stop after seeing &lt;strong&gt;HTTP 200 OK&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Open your CRM.&lt;/p&gt;

&lt;p&gt;Check the pipeline.&lt;/p&gt;

&lt;p&gt;Check the stage.&lt;/p&gt;

&lt;p&gt;Check whether the automation actually reflects how the business operates.&lt;/p&gt;

&lt;p&gt;That's the difference between an integration that works and one that's genuinely useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you ever deployed a CRM integration that technically worked but still created operational problems because records landed in the wrong place? How did you solve the mapping?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>api</category>
      <category>crm</category>
    </item>
    <item>
      <title>Contact Form 7 and Pipedrive Not Talking? Debug the Integration, Not the Form</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Thu, 06 Aug 2026 11:27:28 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/contact-form-7-and-pipedrive-not-talking-debug-the-integration-not-the-form-1o82</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/contact-form-7-and-pipedrive-not-talking-debug-the-integration-not-the-form-1o82</guid>
      <description>&lt;p&gt;A client once reported a simple issue:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"People are submitting the contact form, but nothing is showing up in Pipedrive."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you've worked with WordPress long enough, you've probably heard a variation of that sentence.&lt;/p&gt;

&lt;p&gt;The immediate assumption is usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contact Form 7 is broken.&lt;/li&gt;
&lt;li&gt;WordPress isn't sending emails.&lt;/li&gt;
&lt;li&gt;The CRM plugin has a bug.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But after investigating enough production issues, I've learned something different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most integration failures aren't caused by Contact Form 7. They're caused by the layer that connects Contact Form 7 to the CRM.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A discussion in the Pipedrive Developer Community reflected this exact frustration getting Contact Form 7 to work with Pipedrive wasn't as straightforward as expected. While the technologies may differ from project to project, the debugging process is almost always the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  The form isn't the entire workflow
&lt;/h2&gt;

&lt;p&gt;It's easy to think about the process 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;Visitor
    │
    ▼
Contact Form 7
    │
    ▼
Pipedrive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In reality, several components sit between the form and the CRM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Visitor
    │
    ▼
Contact Form 7
    │
    ▼
HTTP Request
    │
    ▼
Authentication
    │
    ▼
Pipedrive API
    │
    ▼
CRM Record
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If any step in the middle fails, the form can still appear to work while the CRM receives nothing.&lt;/p&gt;

&lt;p&gt;That's why debugging the entire request lifecycle is more important than changing form settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  My first rule: Never assume the API received the data
&lt;/h2&gt;

&lt;p&gt;One mistake I see frequently is treating a successful form submission as proof that the CRM has the data.&lt;/p&gt;

&lt;p&gt;Those are two separate events.&lt;/p&gt;

&lt;p&gt;Before touching any configuration, I ask a few simple questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did Contact Form 7 process the submission?&lt;/li&gt;
&lt;li&gt;Was an HTTP request actually sent?&lt;/li&gt;
&lt;li&gt;Did the API return a response?&lt;/li&gt;
&lt;li&gt;Was authentication successful?&lt;/li&gt;
&lt;li&gt;Did the CRM accept the payload?&lt;/li&gt;
&lt;li&gt;Was the expected record created?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Answering these questions usually narrows the problem down very quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common reasons integrations fail
&lt;/h2&gt;

&lt;p&gt;After debugging several API integrations, I've noticed the same issues appear repeatedly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication problems
&lt;/h3&gt;

&lt;p&gt;An expired API token or incorrect credentials can cause every request to fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Incorrect endpoint
&lt;/h3&gt;

&lt;p&gt;Sending data to the wrong API endpoint means the CRM either rejects the request or creates an unexpected result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Payload mismatch
&lt;/h3&gt;

&lt;p&gt;The CRM may expect fields that the form isn't sending, or it may reject values that don't match its schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network or timeout issues
&lt;/h3&gt;

&lt;p&gt;Sometimes the request never reaches the CRM because of connectivity problems or server timeouts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing request visibility
&lt;/h3&gt;

&lt;p&gt;This is probably the biggest challenge.&lt;/p&gt;

&lt;p&gt;Without logs, you're left asking:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Did the request ever leave WordPress?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And that's not a good place to start.&lt;/p&gt;

&lt;h2&gt;
  
  
  The integration layer deserves more attention
&lt;/h2&gt;

&lt;p&gt;Contact Form 7 does exactly what it's designed to do it collects user input.&lt;/p&gt;

&lt;p&gt;Everything after that depends on the integration.&lt;/p&gt;

&lt;p&gt;That's why I think developers should spend less time replacing form plugins and more time improving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request logging&lt;/li&gt;
&lt;li&gt;payload validation&lt;/li&gt;
&lt;li&gt;authentication handling&lt;/li&gt;
&lt;li&gt;API response monitoring&lt;/li&gt;
&lt;li&gt;retry strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These improvements make integrations easier to support and far more reliable in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a more resilient workflow
&lt;/h2&gt;

&lt;p&gt;When integrating Contact Form 7 with a CRM like Pipedrive, I aim for a workflow that 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;Submit Form
      │
      ▼
Validate Data
      │
      ▼
Send API Request
      │
      ▼
Log Request &amp;amp; Response
      │
      ▼
Create CRM Record
      │
      ▼
Handle Failures Gracefully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That extra visibility pays for itself the first time something goes wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Contact Form to API fits
&lt;/h2&gt;

&lt;p&gt;If you're using Contact Form 7 and need more control over API integrations, &lt;strong&gt;Contact Form to API&lt;/strong&gt; focuses on the integration layer rather than replacing your forms.&lt;/p&gt;

&lt;p&gt;It allows developers to send submissions directly to external APIs while configuring endpoints, authentication, headers, and request payloads to match the target system.&lt;/p&gt;

&lt;p&gt;If you're working with Pipedrive, these resources are worth exploring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easily Connect Contact Form 7 with Pipedrive&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/easily-connect-contact-form-7-with-pipedrive/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/easily-connect-contact-form-7-with-pipedrive/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contact Form 7 Data Not Reaching API&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contact Form 7 API Authentication Error&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/contact-form-7-api-authentication-error/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;When an integration fails, it's tempting to blame the form because that's the part users interact with.&lt;/p&gt;

&lt;p&gt;In reality, the form is usually doing its job.&lt;/p&gt;

&lt;p&gt;The real challenge is ensuring the API request reaches the CRM, is accepted, and creates the expected record.&lt;/p&gt;

&lt;p&gt;The next time someone says, &lt;strong&gt;"Contact Form 7 isn't working,"&lt;/strong&gt; don't start by replacing the plugin.&lt;/p&gt;

&lt;p&gt;Start by tracing the request.&lt;/p&gt;

&lt;p&gt;You'll often discover that the problem isn't the form at all—it's the integration between the form and the CRM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you debug Contact Form 7 integrations in production?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do you rely on request logs, API monitoring, webhook testing, or another workflow? I'd be interested to hear how other developers approach these issues.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>api</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Your API Worked Perfectly… It Just Created the Wrong CRM Record</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Wed, 05 Aug 2026 11:08:57 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/your-api-worked-perfectly-it-just-created-the-wrong-crm-record-4n97</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/your-api-worked-perfectly-it-just-created-the-wrong-crm-record-4n97</guid>
      <description>&lt;p&gt;One of the easiest mistakes to make when integrating a CRM isn't a failed API request.&lt;/p&gt;

&lt;p&gt;It's a successful one.&lt;/p&gt;

&lt;p&gt;Recently, I came across a discussion from a developer integrating Contact Form 7 with Pipedrive. Every submission from the website reached the CRM successfully, but instead of creating a &lt;strong&gt;Deal&lt;/strong&gt;, it created a &lt;strong&gt;Contact (Person)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At first glance, everything looked correct.&lt;/p&gt;

&lt;p&gt;The API returned a success response.&lt;/p&gt;

&lt;p&gt;No errors were logged.&lt;/p&gt;

&lt;p&gt;The data reached Pipedrive.&lt;/p&gt;

&lt;p&gt;Yet the sales team couldn't use the records because every enquiry was ending up in the wrong place.&lt;/p&gt;

&lt;p&gt;This is a good reminder that a &lt;strong&gt;successful HTTP request doesn't always mean you've achieved the business outcome you wanted.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The API did exactly what it was told
&lt;/h2&gt;

&lt;p&gt;When developers see the wrong object being created, the first instinct is often to blame the form plugin.&lt;/p&gt;

&lt;p&gt;But Contact Form 7 isn't responsible for deciding whether a CRM should create a Contact, Lead, Deal, or Opportunity.&lt;/p&gt;

&lt;p&gt;Its job is much simpler.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="2q9rka"&lt;br&gt;
Collect Form Data&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
Pass Data Forward&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Everything after that depends on the integration.

The API only processes the request it receives.

If the request targets the Contacts endpoint, the CRM creates a Contact.

If it targets the Deals endpoint, it creates a Deal.

From the API's perspective, both requests are perfectly valid.

## CRMs organize data differently

Almost every modern CRM separates different business entities.

For example:



```text id="mr4c5u"
Person
Organization
Lead
Deal
Activity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Although they're related, each object serves a different purpose.&lt;/p&gt;

&lt;p&gt;A Contact identifies a person.&lt;/p&gt;

&lt;p&gt;A Deal represents a sales opportunity.&lt;/p&gt;

&lt;p&gt;An Organization groups companies.&lt;/p&gt;

&lt;p&gt;An Activity tracks work.&lt;/p&gt;

&lt;p&gt;Choosing the wrong endpoint changes the entire workflow.&lt;/p&gt;

&lt;p&gt;That's why developers should understand the CRM's data model before writing any integration code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Success isn't just about HTTP 200
&lt;/h2&gt;

&lt;p&gt;Imagine this request.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```http id="9k2vbm"&lt;br&gt;
POST /persons&lt;/p&gt;

&lt;p&gt;HTTP/1.1 200 OK&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Technically, everything worked.

But what if the client expected:



```text id="0cfd0o"
Website Enquiry
        │
        ▼
New Deal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Instead, they got:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="jny7ns"&lt;br&gt;
Website Enquiry&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
New Contact&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The API isn't broken.

The integration logic is.

This is why I try to separate **technical success** from **business success** whenever I'm reviewing integrations.

## Before writing code, understand the workflow

When integrating Contact Form 7 with any CRM, I always ask one question first:

&amp;gt; **What should happen after someone submits this form?**

Different forms often have different answers.

A contact form might create a Contact.

A demo request might create a Deal.

A support request might create a Ticket.

A partnership enquiry might create an Organization.

Treating every form submission the same usually creates problems later.

Instead, start with the business workflow and then map the API around it.

## Payloads matter just as much as endpoints

Even when you've selected the correct endpoint, the payload still needs to match what the API expects.

For example:



```json id="j5h8tx"
{
  "title": "Website Demo Request",
  "person_id": 123,
  "pipeline_id": 2,
  "stage_id": 5
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Deal typically requires different fields than a Contact.&lt;/p&gt;

&lt;p&gt;Without the required values, the CRM may reject the request or create incomplete records.&lt;/p&gt;

&lt;p&gt;Whenever I'm debugging integrations, I check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;endpoint&lt;/li&gt;
&lt;li&gt;HTTP method&lt;/li&gt;
&lt;li&gt;required fields&lt;/li&gt;
&lt;li&gt;JSON structure&lt;/li&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;object relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those six things solve far more problems than changing the form itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build integrations that match the CRM
&lt;/h2&gt;

&lt;p&gt;One lesson I've learned over the years is that APIs shouldn't dictate your business process.&lt;/p&gt;

&lt;p&gt;Your business process should dictate how you use the API.&lt;/p&gt;

&lt;p&gt;If your sales team works with Deals, then your integration should create Deals.&lt;/p&gt;

&lt;p&gt;If marketing works with Leads, then create Leads.&lt;/p&gt;

&lt;p&gt;If support manages Tickets, then create Tickets.&lt;/p&gt;

&lt;p&gt;The integration should reflect how people actually work inside the CRM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Contact Form to API helps
&lt;/h2&gt;

&lt;p&gt;This is one area where &lt;strong&gt;Contact Form to API&lt;/strong&gt; gives developers much more flexibility than fixed CRM connectors.&lt;/p&gt;

&lt;p&gt;Instead of forcing every Contact Form 7 submission into a predefined workflow, it allows you to send requests to custom API endpoints with payloads that match your CRM's requirements.&lt;/p&gt;

&lt;p&gt;That means you're not limited to creating one type of object.&lt;/p&gt;

&lt;p&gt;You decide which endpoint receives the request and how the data is structured.&lt;/p&gt;

&lt;p&gt;If you're working with Pipedrive, these guides are a good starting point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easily Connect Contact Form 7 with Pipedrive&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/easily-connect-contact-form-7-with-pipedrive/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/easily-connect-contact-form-7-with-pipedrive/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contact Form 7 JSON Mapping&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/contact-form-7-json-mapping/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/contact-form-7-json-mapping/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contact Form 7 CRM Integration Guide&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/contact-form-7-crm-integration/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/contact-form-7-crm-integration/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;One of the best lessons API integrations teach us is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A successful request isn't always a successful integration.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The server can return &lt;strong&gt;200 OK&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The CRM can create a record.&lt;/p&gt;

&lt;p&gt;Everything can look technically correct.&lt;/p&gt;

&lt;p&gt;Yet the business workflow can still be completely wrong because the integration created the wrong object.&lt;/p&gt;

&lt;p&gt;Before blaming Contact Form 7 or even the CRM take a step back and inspect the integration itself.&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did I choose the correct endpoint?&lt;/li&gt;
&lt;li&gt;Does this object match the client's workflow?&lt;/li&gt;
&lt;li&gt;Is my payload aligned with the CRM's schema?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those three questions will prevent far more problems than endlessly tweaking form settings.&lt;/p&gt;

&lt;p&gt;Have you ever integrated a CRM only to discover it was creating the wrong records? Was the issue the endpoint, the payload, or something else entirely? I'd love to hear how you solved it.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>api</category>
      <category>crm</category>
    </item>
    <item>
      <title>The API Failed. The Customer Never Knew. Here's Why Logging Matters</title>
      <dc:creator>Rahul Sharma</dc:creator>
      <pubDate>Mon, 03 Aug 2026 11:38:41 +0000</pubDate>
      <link>https://dev.to/rahul_sharma_15bd129bc69e/the-api-failed-the-customer-never-knew-heres-why-logging-matters-538l</link>
      <guid>https://dev.to/rahul_sharma_15bd129bc69e/the-api-failed-the-customer-never-knew-heres-why-logging-matters-538l</guid>
      <description>&lt;p&gt;One of the most frustrating bugs I've debugged wasn't a PHP exception or a broken API endpoint.&lt;/p&gt;

&lt;p&gt;It was a &lt;strong&gt;successful&lt;/strong&gt; Contact Form 7 submission.&lt;/p&gt;

&lt;p&gt;At least, that's what everyone thought.&lt;/p&gt;

&lt;p&gt;The user filled out the form, clicked &lt;strong&gt;Submit&lt;/strong&gt;, received the success message, and left the website.&lt;/p&gt;

&lt;p&gt;A few days later, the client asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why isn't this lead in our CRM?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question immediately changes the investigation.&lt;/p&gt;

&lt;p&gt;You're no longer debugging Contact Form 7.&lt;/p&gt;

&lt;p&gt;You're debugging everything that happens &lt;strong&gt;after&lt;/strong&gt; Contact Form 7.&lt;/p&gt;

&lt;h2&gt;
  
  
  The success message lies (sometimes)
&lt;/h2&gt;

&lt;p&gt;When Contact Form 7 displays:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Your message has been sent successfully.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;it only confirms one thing.&lt;/p&gt;

&lt;p&gt;The form submission was accepted by WordPress.&lt;/p&gt;

&lt;p&gt;It doesn't guarantee that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the API request was sent&lt;/li&gt;
&lt;li&gt;authentication succeeded&lt;/li&gt;
&lt;li&gt;the CRM accepted the payload&lt;/li&gt;
&lt;li&gt;the lead was created&lt;/li&gt;
&lt;li&gt;downstream automations completed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are completely different steps.&lt;/p&gt;

&lt;p&gt;A typical workflow actually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="d3v1ab"&lt;br&gt;
Visitor&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
Contact Form 7&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
API Request&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
CRM API&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
Lead Created&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


If the request fails anywhere after the form submission, the visitor will probably never know.

Unfortunately, neither will the business.

## APIs fail for perfectly normal reasons

When developers first build integrations, it's easy to assume:



```text id="z5m8cn"
Submit
   ↓
Success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Production environments are rarely that simple.&lt;/p&gt;

&lt;p&gt;An API request can fail because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication tokens expired&lt;/li&gt;
&lt;li&gt;endpoint URLs changed&lt;/li&gt;
&lt;li&gt;required fields are missing&lt;/li&gt;
&lt;li&gt;payload validation failed&lt;/li&gt;
&lt;li&gt;the API is rate limited&lt;/li&gt;
&lt;li&gt;the server timed out&lt;/li&gt;
&lt;li&gt;temporary network issues occurred&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those mean your integration is poorly written.&lt;/p&gt;

&lt;p&gt;They're simply realities of working with external services.&lt;/p&gt;

&lt;p&gt;The real question becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can your integration tell you exactly what happened?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Debugging without logs is mostly guessing
&lt;/h2&gt;

&lt;p&gt;Imagine receiving this support ticket:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Five enquiries never reached Insightly."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without logs, your investigation usually sounds like this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Maybe the CRM was unavailable?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Maybe SMTP failed?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Maybe Contact Form 7 didn't submit?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Maybe WordPress cached something?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Notice how every sentence starts with &lt;strong&gt;maybe&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now imagine having request logs.&lt;/p&gt;

&lt;p&gt;Instead, you immediately see:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```http id="c8n4fd"&lt;br&gt;
POST /v3.1/Contacts&lt;br&gt;
HTTP 401 Unauthorized&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


or



```http id="m2x7qe"
HTTP 408 Request Timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```http id="r4w1jy"&lt;br&gt;
HTTP 422 Validation Error&lt;br&gt;
company_name is required&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The debugging process changes completely.

You're no longer guessing.

You're reading evidence.

## Every API integration should answer three questions

Whenever I integrate Contact Form 7 with an external system, I want the workflow to answer three things immediately.

### 1. Was the request sent?



```text id="k7f3we"
Contact Form 7
      │
      ▼
HTTP Request
      ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. What response came back?
&lt;/h3&gt;



&lt;p&gt;```http id="v1h9ra"&lt;br&gt;
HTTP 200 OK&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


or



```http id="y8b2ld"
HTTP 401 Unauthorized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Can I resend it?
&lt;/h3&gt;

&lt;p&gt;Because temporary failures shouldn't force customers to submit the form again.&lt;/p&gt;

&lt;p&gt;If authentication expired or the CRM experienced downtime, I'd much rather fix the issue and replay the original request.&lt;/p&gt;

&lt;p&gt;The customer shouldn't even know something went wrong.&lt;/p&gt;
&lt;h2&gt;
  
  
  Retry support is more valuable than people think
&lt;/h2&gt;

&lt;p&gt;Imagine your CRM goes offline for fifteen minutes.&lt;/p&gt;

&lt;p&gt;During that period:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="t6m2bo"&lt;br&gt;
Lead 1 ❌&lt;br&gt;
Lead 2 ❌&lt;br&gt;
Lead 3 ❌&lt;br&gt;
Lead 4 ❌&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Without retry functionality:

Those leads are gone.

With retry functionality:



```text id="a9q7uv"
Issue Fixed
      │
      ▼
Replay Requests
      │
      ▼
All Leads Delivered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a completely different outcome.&lt;/p&gt;

&lt;p&gt;The customer experience remains unchanged.&lt;/p&gt;

&lt;p&gt;The business keeps its enquiries.&lt;/p&gt;

&lt;p&gt;Developers spend less time explaining what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improving the integration layer
&lt;/h2&gt;

&lt;p&gt;One thing I've learned is that Contact Form 7 is rarely the problem.&lt;/p&gt;

&lt;p&gt;It already does its job well.&lt;/p&gt;

&lt;p&gt;The weak point is usually the integration layer between WordPress and the external API.&lt;/p&gt;

&lt;p&gt;That's the part that deserves attention.&lt;/p&gt;

&lt;p&gt;Instead of replacing the form plugin, I prefer improving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request visibility&lt;/li&gt;
&lt;li&gt;payload validation&lt;/li&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;error logging&lt;/li&gt;
&lt;li&gt;retry handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those improvements produce much more reliable systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Contact Form to API fits
&lt;/h2&gt;

&lt;p&gt;If you're already using Contact Form 7, &lt;strong&gt;Contact Form to API&lt;/strong&gt; focuses on exactly that integration layer.&lt;/p&gt;

&lt;p&gt;Rather than replacing your forms, it helps send submissions directly to external APIs while giving developers better visibility into requests and failures.&lt;/p&gt;

&lt;p&gt;If you've ever encountered situations where submissions weren't reaching an external system, these guides are worth bookmarking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contact Form 7 Data Not Reaching API&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/contact-form-7-data-not-reaching-api/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contact Form 7 API Authentication Error&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/contact-form-7-api-authentication-error/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/contact-form-7-api-authentication-error/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contact Form 7 API Timeout&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.contactformtoapi.com/contact-form-7-api-timeout/" rel="noopener noreferrer"&gt;https://www.contactformtoapi.com/contact-form-7-api-timeout/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;One successful API request doesn't prove an integration is reliable.&lt;/p&gt;

&lt;p&gt;The real test is what happens when something fails.&lt;/p&gt;

&lt;p&gt;Can you inspect the request?&lt;/p&gt;

&lt;p&gt;Can you identify the response?&lt;/p&gt;

&lt;p&gt;Can you replay the submission?&lt;/p&gt;

&lt;p&gt;If the answer is yes, you've built something that's much easier to support in production.&lt;/p&gt;

&lt;p&gt;Because the goal isn't simply to send data.&lt;/p&gt;

&lt;p&gt;It's to make sure valuable data never disappears without a trace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you handle failed API requests in your WordPress projects?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do you log every request, implement retries, or rely on external monitoring? I'd love to hear how other developers approach production integrations.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>api</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
