A developer documented a real CF7 to Freshdesk integration in a 2017 tutorial. The comments section became a support thread multiple people following the same code reported that "it just sends the email but no activity on Freshdesk." The author's debugging advice: use Postman to test the API directly, then print the PHP array to check the values.
That debugging advice is still the right starting point. Here are the four causes that produce the same symptom form submits, email arrives, no Freshdesk ticket.
Cause 1: Wrong Domain Format in the API URL
Freshdesk API calls require your full subdomain in the URL:
https://YOUR_SUBDOMAIN.freshdesk.com/api/v2/tickets
Where YOUR_SUBDOMAIN is the part before .freshdesk.com in your Freshdesk URL. If your Freshdesk account is at acmehelp.freshdesk.com, the API endpoint is:
https://acmehelp.freshdesk.com/api/v2/tickets
The most common mistake is entering just the subdomain (acmehelp) without the full domain, or using freshdesk.com without the subdomain prefix. Both produce a URL that either does not resolve or returns a generic Freshdesk error page rather than an API response.
Test your domain format:
curl -v "https://YOUR_SUBDOMAIN.freshdesk.com/api/v2/tickets" \
-u "YOUR_API_KEY:X"
If you see a JSON response (even a 401), the URL format is correct. If you see an HTML page or a connection error, the domain format is wrong.
Cause 2: API Key Authentication Format Is Not Standard
Freshdesk uses HTTP Basic authentication but with a non-standard format. The username is your API key and the password is literally the letter X:
Authorization: Basic base64(YOUR_API_KEY:X)
This is different from most APIs. The API key goes in the username position. The password is always X not your actual password, not empty, literally the single character X.
Using wp_remote_post in PHP:
'headers' => [
'Authorization' => 'Basic ' . base64_encode(FRESHDESK_API_KEY . ':X'),
'Content-Type' => 'application/json',
],
If you enter your email and password in the Basic auth fields instead of API_KEY:X, authentication will fail with a 401 even if the credentials are otherwise correct.
Find your Freshdesk API key: In Freshdesk, click your profile picture at the top right, then Profile Settings. The API Key appears at the bottom right of the page.
Cause 3: Required Fields Missing from the Ticket Payload
Freshdesk requires at minimum two fields for every ticket:
-
subject— the ticket title/subject line -
email— the requester's email address
Without both, Freshdesk returns a 422 Unprocessable Entity with a validation error. If your plugin or custom code maps only the message body or only the name, the ticket creation fails.
A minimal valid Freshdesk ticket payload:
{
"subject": "Enquiry from Jane Smith",
"description": "Message content here",
"email": "jane@example.com",
"status": 2,
"priority": 1
}
Freshdesk status codes:
-
1= Open -
2= Pending -
3= Resolved -
4= Closed
Freshdesk priority codes:
-
1= Low -
2= Medium -
3= High -
4= Urgent
For most CF7 contact form tickets, status: 2 (Pending) and priority: 1 (Low) are appropriate defaults.
Cause 4: The Hook wpcf7_mail_sent vs wpcf7_before_send_mail
The original tutorial code used wpcf7_mail_sent as the hook:
add_action('wpcf7_mail_sent', 'cf7_create_freshdesk_ticket', 0, 1);
wpcf7_mail_sent fires only after CF7 has successfully sent its notification email. If CF7's mail sending fails for any reason — SMTP configuration issues, email blocked by spam filter, missing mail configuration — this hook never fires and no Freshdesk ticket is created.
The more reliable hook for API integrations is wpcf7_before_send_mail:
add_action('wpcf7_before_send_mail', 'cf7_create_freshdesk_ticket');
This fires during the submission processing regardless of whether the email is sent successfully. For use cases where the Freshdesk ticket should always be created when someone submits the form, not only when the email delivery succeeds, use wpcf7_before_send_mail.
Complete CF7 to Freshdesk Implementation
add_action('wpcf7_before_send_mail', 'cf7_create_freshdesk_ticket');
function cf7_create_freshdesk_ticket($contact_form) {
if ((int) $contact_form->id() !== YOUR_FORM_ID) return;
$submission = WPCF7_Submission::get_instance();
if (!$submission) return;
$data = $submission->get_posted_data();
$name = sanitize_text_field($data['your-name'] ?? '');
$email = sanitize_email($data['your-email'] ?? '');
$message = sanitize_textarea_field($data['your-message'] ?? '');
if (empty($email)) return;
$api_key = defined('FRESHDESK_API_KEY') ? FRESHDESK_API_KEY : '';
$subdomain = defined('FRESHDESK_SUBDOMAIN') ? FRESHDESK_SUBDOMAIN : '';
$response = wp_remote_post(
"https://{$subdomain}.freshdesk.com/api/v2/tickets",
[
'headers' => [
'Authorization' => 'Basic ' . base64_encode($api_key . ':X'),
'Content-Type' => 'application/json',
],
'body' => wp_json_encode([
'subject' => 'Enquiry from ' . $name,
'description' => $message,
'email' => $email,
'status' => 2,
'priority' => 1,
]),
'timeout' => 15,
]
);
if (is_wp_error($response)) {
error_log('[CF7->Freshdesk] Error: ' . $response->get_error_message());
return;
}
$status = wp_remote_retrieve_response_code($response);
error_log('[CF7->Freshdesk] Response: ' . $status . ' — ' . wp_remote_retrieve_body($response));
}
Store credentials in wp-config.php:
define('FRESHDESK_API_KEY', 'your-api-key-here');
define('FRESHDESK_SUBDOMAIN', 'yourcompany');
No-Code Alternative
Contact Form to API handles the Freshdesk API call from the WordPress dashboard. Configure:
-
Endpoint:
https://yoursubdomain.freshdesk.com/api/v2/tickets - Method: POST
-
Authorization:
Basic+ base64 ofYOUR_API_KEY:X -
Body: JSON with
subject,email,description,status,priority
The response is logged for every submission so you see the Freshdesk ticket ID or the exact validation error if something fails.
Quick Diagnosis
| Error | Cause | Fix |
|---|---|---|
| Connection error / HTML response | Wrong domain format in URL | Use https://SUBDOMAIN.freshdesk.com/api/v2/tickets
|
401 Unauthorized |
Wrong auth format | Use API_KEY:X as Basic auth, not email:password |
422 Unprocessable Entity |
Missing subject or email
|
Include both required fields in payload |
| No ticket, no error | Hook wpcf7_mail_sent not firing |
Switch to wpcf7_before_send_mail
|
| Ticket created but no requester | Email field not mapped | Confirm email key is in the payload |
Top comments (0)