Getting a 400 Bad Request From Your CF7 Webhook? Here Is What Is Actually Wrong

You set up a CF7 webhook to send form submissions to an external API. You test it and get a 400 Bad Request error. The debug log shows something that looks like broken JSON. You spend hours wondering if your plugin has an encoding bug. You post on forums. You get a partial answer that does not fully solve it.
This is exactly what happened to a developer who posted on the WordPress support forums. They were connecting CF7 to the RO App CRM API and getting validation errors saying required fields were empty. The debug log made it look like the JSON was being double-encoded. It was not. The real problem was simpler and easier to fix.
This post explains what the debug log is actually showing you, why 400 errors almost always come from wrong field names rather than encoding issues, and how to get your CF7 webhook sending data correctly the first time.
The Debug Log Is Not Showing You What You Think It Is
When a CF7 webhook plugin sends you a failure notification email, it includes a section called Request Body. It looks something like this:
Request Body:
"{\n \"contact_name\": \"John Doe\",\n \"contact_phone\": \"123456789\"\n}"
The outer quotes and the backslash characters make it look like something has gone wrong with the JSON. It looks like a string inside a string. It looks like the data has been wrapped in extra quotes.
Nothing is wrong. This is just how PHP represents a string that contains quote marks when it prints it for logging. The actual data that was sent to the API over the network was proper, valid JSON that looked like this:
{
"contact_name": "John Doe",
"contact_phone": "123456789"
}
The API received exactly that. The encoding was fine all along. So if the encoding was fine, why did the API return 400?
Why the API Said the Required Field Was Empty
The response body from the API gave the real answer:
{
"code": 400,
"success": false,
"message": {
"validation": {
"leadtype_id": ["Field cannot be empty"]
}
}
}
The developer had used _notify_leadtype_id as the field name in their webhook body. The correct field name, according to the API documentation, was leadtype_id.
The API received the request, parsed the JSON without any problem, looked for a field called leadtype_id, did not find it, and returned a validation error saying it was empty. The field was not empty. It just had the wrong name, so the API never saw it.
A wrong field name is the most common cause of 400 validation errors in CF7 webhooks. The API is not broken. The encoding is not broken. The field name just does not match what the API expects.
How to Find the Right Field Names Every Time
Every API has documentation that lists the exact field names it expects in the request body. These names are case-sensitive and exact. leadtype_id, lead_type_id, and LeadTypeId are three completely different things to an API.
Before you set up any CF7 webhook, open the API reference for the specific endpoint you are calling. Find the request body parameters section and copy the field names exactly as they appear. Do not type them from memory and do not copy them from an example in a forum post. Go directly to the official docs and copy from there.
If the API documentation is not clear about field names, test the endpoint directly with a tool like Postman or cURL before connecting your form. This tells you within seconds whether your field names and credentials are correct.
The Other Part of the Problem: Sending a Fixed Value Alongside Form Fields
The developer also needed to send leadtype_id with a fixed number (318825) that never changes. This is not a value that comes from the form. It is a static ID that tells the CRM what category the lead belongs to.
Many CF7 webhook plugins only support dynamic values from form fields. They do not have a way to add a hardcoded static value to the request body alongside the form data. When you try to send a fixed value, it either gets sent as the placeholder text or it gets left out entirely. Either way the required field appears empty to the API and you get a 400.
Contact Form to API handles this correctly. You can define static values that are always included in the request body alongside dynamic values that come from your CF7 form fields. So you can set leadtype_id to 318825 permanently and map contact_name and contact_phone from what the visitor fills in. The plugin builds the complete request body correctly and sends it to your API as a proper JSON object.
This is exactly the missing piece in the forum thread. The developer had the right idea. They just needed a plugin that could combine fixed values and form field values in the same API call without breaking either one.
What to Check When You Get a 400 Error
When your CF7 webhook returns a 400 Bad Request, go through these steps before assuming anything is broken with your plugin or your JSON formatting.
Read the response body carefully. APIs that return 400 almost always include a validation message that tells you exactly which field is wrong or missing. That message is your fastest path to the fix.
Compare every field name in your webhook body against the official API documentation. Look for typos, underscores in the wrong place, wrong capitalisation. One character difference is enough to cause the whole request to fail.
Check whether you have any static values that need to be in the request. If the API requires a category ID or a type ID that does not come from your form, make sure your plugin can include hardcoded values alongside dynamic form fields.
Test the API call directly before connecting CF7. Use cURL or Postman with your exact field names and a sample payload. If it works there, you know the problem is in the webhook configuration, not in your understanding of the API.




