SendGrid Custom Integration WP
August 7, 2025 · AI Conversations
—
### ✅ Goal:
When a user submits a custom form on your WordPress site, their email (and other details) should be **added to a SendGrid Marketing List** using the **SendGrid v3 API** — all done via PHP or AJAX, **no iframe**.
—
### ✅ Steps to Integrate Custom Newsletter Form with SendGrid
—
#### 🔹 1. **Create a Marketing List in SendGrid**
– Go to [SendGrid → Marketing → Contacts](https://app.sendgrid.com/marketing/contacts)
– Click **"Create List"** and name it (e.g., `Website Subscribers`)
– Note down the List ID (you'll fetch this via API in the next steps)
—
#### 🔹 2. **Generate SendGrid API Key**
– Go to [SendGrid → Settings → API Keys](https://app.sendgrid.com/settings/api_keys)
– Create a new key with **Full Access** to "Marketing"
– Save this API key securely (we’ll use it in code)
—
#### 🔹 3. **Create a Custom Form in WordPress**
Example form (use in a page, widget, or template):
<form id="newsletter-form">
<input type="email" name="email" placeholder="Enter your email" required />
<input type="text" name="first_name" placeholder="First Name" />
<button type="submit">Subscribe</button>
<div id="newsletter-result"></div>
</form>
—
#### 🔹 4. **Add AJAX Submission to WordPress (Enqueue JS)**
Add to your theme’s JS (or inline):
jQuery(document).ready(function ($) {
$('#newsletter-form').on('submit', function (e) {
e.preventDefault();
var formData = $(this).serialize();
$.post({
url: newsletter_form_ajax.ajax_url,
data: {
action: 'sendgrid_subscribe',
formData: formData,
},
success: function (response) {
$('#newsletter-result').html(response.message);
},
});
});
});
Add this enqueue in your `functions.php`:
add_action('wp_enqueue_scripts', function () {
wp_enqueue_script('newsletter-form', get_template_directory_uri() . '/js/newsletter.js', ['jquery'], null, true);
wp_localize_script('newsletter-form', 'newsletter_form_ajax', ['ajax_url' => admin_url('admin-ajax.php')]);
});
—
#### 🔹 5. **Handle AJAX in `functions.php` (Server-side Call to SendGrid)**
add_action('wp_ajax_sendgrid_subscribe', 'handle_sendgrid_subscribe');
add_action('wp_ajax_nopriv_sendgrid_subscribe', 'handle_sendgrid_subscribe');
function handle_sendgrid_subscribe() {
parse_str($_POST['formData'], $data);
$email = sanitize_email($data['email']);
$first_name = sanitize_text_field($data['first_name'] ?? '');
if (!is_email($email)) {
wp_send_json(['success' => false, 'message' => 'Invalid email']);
}
$apiKey = 'YOUR_SENDGRID_API_KEY';
$listId = 'YOUR_SENDGRID_LIST_ID';
$contactData = [
'list_ids' => [$listId],
'contacts' => [[
'email' => $email,
'first_name' => $first_name,
]]
];
$response = wp_remote_post('https://api.sendgrid.com/v3/marketing/contacts', [
'headers' => [
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json',
],
'body' => json_encode($contactData),
]);
if (is_wp_error($response)) {
wp_send_json(['success' => false, 'message' => 'Request failed']);
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (isset($body['job_id'])) {
wp_send_json(['success' => true, 'message' => 'Thank you for subscribing!']);
} else {
wp_send_json(['success' => false, 'message' => 'Unable to subscribe']);
}
}
—
### 🔐 Security Tips
– Store the SendGrid API key securely, optionally using `wp-config.php`.
– Rate-limit or add CAPTCHA to prevent spam.
—
### ✅ Final Output
A fully custom newsletter form that:
– Submits via AJAX
– Adds users directly to your SendGrid Marketing List
– Doesn’t require iframe or Marketing → Signup Form builder
—
If you need this to work via REST API instead of `admin-ajax.php` (for headless WordPress or JS apps), I can help you set that up too.
Would you like a plugin version of this for reuse across projects?
—
### ✅ What is `YOUR_SENDGRID_LIST_ID`?
This is the **unique ID** of a list (e.g., "Website Subscribers") created inside **SendGrid Marketing Campaigns**.
It's required when using the **SendGrid Marketing API** to add contacts programmatically.
—
### 🔍 How to Get the List ID?
#### 🔹 Option 1: **Using the SendGrid API** (Recommended)
SendGrid doesn’t show the List ID in the UI directly, so we use the **List API**:
##### ✅ Make this cURL request (replace your actual API key):
curl --request GET
--url https://api.sendgrid.com/v3/marketing/lists
--header "Authorization: Bearer YOUR_SENDGRID_API_KEY"
--header "Content-Type: application/json"
##### 🧠 Response Example:
{
"result": [
{
"id": "9b2d7a3f-8f63-4f8b-a455-xxxxx",
"name": "Website Subscribers",
...
}
]
}
➡️ Copy the `id` field — that is your **`YOUR_SENDGRID_LIST_ID`**
—
#### 🔹 Option 2: **Use Postman (No Coding)**
1. Open [Postman](https://www.postman.com/)
2. Create a `GET` request to:
https://api.sendgrid.com/v3/marketing/lists
3. Add Header:
– `Authorization: Bearer YOUR_SENDGRID_API_KEY`
– `Content-Type: application/json`
4. Hit **Send** — you'll get a list of all your lists with `id` values.
—
#### 🔹 Option 3: Create a New List and Capture ID Immediately
You can also create a list via API and note the returned `id`:
curl --request POST
--url https://api.sendgrid.com/v3/marketing/lists
--header "Authorization: Bearer YOUR_SENDGRID_API_KEY"
--header "Content-Type: application/json"
--data '{
"name": "Website Subscribers"
}'
—
### 📌 Pro Tip
Once you get the ID, store it in `wp-config.php` for easy use:
define('SENDGRID_LIST_ID', '9b2d7a3f-8f63-4f8b-a455-xxxx');
Then in your code:
$listId = constant('SENDGRID_LIST_ID');
—
Would you like me to generate a ready-to-run API snippet or Postman collection for you?
/contacts/lists/f3eaa89e-b043-4652-9b37-7eb45501b7b6
—
### ✅ Your `YOUR_SENDGRID_LIST_ID` is:
f3eaa89e-b043-4652-9b37-7eb45501b7b6
—
### 🔧 How to Use in Your WordPress Code:
Replace this:
$listId = 'YOUR_SENDGRID_LIST_ID';
With this:
$listId = 'f3eaa89e-b043-4652-9b37-7eb45501b7b6';
And you’re all set! 🎉
This will ensure contacts added via your custom form go directly into the right **SendGrid Marketing List**.
Let me know if you want to add more fields like `last_name`, `custom_fields`, or tag contacts — I can help with that too.
check_ajax_referer('newsletter_nonce', 'nonce');
$email = sanitize_email($_POST['email']);
$regular = sanitize_text_field($_POST['regular']);
$specific = sanitize_text_field($_POST['specific']);
$sendgrid_api_key = 'SG.Et-F1hGBQ924bE-jjmTkccQPGLudM';
$listId = 'f3eaa89e-b043-4652-9b37-7eb45501b7b6';
$body = [
'personalizations' => [[
'to' => [['email' => 'rahulratnam@nethues.com']],
'subject' => 'New Newsletter Signup',
]],
'from' => ['email' => 'no-reply@professor.com'],
'content' => [[
'type' => 'text/plain',
'value' => "New Signup:nEmail: $emailnRegular: $regularnSpecific: $specific",
]]
];
$response = wp_remote_post('https://api.sendgrid.com/v3/mail/send', [
'headers' => [
'Authorization' => 'Bearer ' . $sendgrid_api_key,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode($body),
]);
// echo '';
// print_r($response);
// die;
if (is_wp_error($response)) {
wp_send_json(['message' => '❌ Failed to send email.']);
}
wp_send_json(['message' => '✅ Thank you! You are signed up.']);
exit;
}
adjust in above code
Here’s how to **adjust your `handle_newsletter_form()` function** to:
1. ✅ Add the contact to SendGrid Marketing List.
2. 📧 Send notification email to admin.
—
### ✅ Final Updated Code
public function handle_newsletter_form() {
check_ajax_referer('newsletter_nonce', 'nonce');
$email = sanitize_email($_POST['email']);
$regular = sanitize_text_field($_POST['regular']);
$specific = sanitize_text_field($_POST['specific']);
$sendgrid_api_key = 'SG.Et-F1hGBQ924bE-jjmTkccQPGLudM';
$listId = 'f3eaa89e-b043-4652-9b37-7eb45501b7b6';
// ✅ 1. Add contact to SendGrid List
$contactData = [
'list_ids' => [$listId],
'contacts' => [[
'email' => $email,
'custom_fields' => [
// Optional: if you have custom fields, add them here by custom field ID
// e.g. 'e1_T' => $regular,
// 'e2_T' => $specific
]
]]
];
$add_response = wp_remote_post('https://api.sendgrid.com/v3/marketing/contacts', [
'headers' => [
'Authorization' => 'Bearer ' . $sendgrid_api_key,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode($contactData),
]);
if (is_wp_error($add_response)) {
wp_send_json(['message' => '❌ Failed to add to newsletter list.']);
}
// ✅ 2. Send admin notification email
$emailBody = [
'personalizations' => [[
'to' => [['email' => 'rahulratnam@nethues.com']],
'subject' => 'New Newsletter Signup',
]],
'from' => ['email' => 'no-reply@professor.com'],
'content' => [[
'type' => 'text/plain',
'value' => "New Signup:nEmail: $emailnRegular: $regularnSpecific: $specific",
]]
];
$mail_response = wp_remote_post('https://api.sendgrid.com/v3/mail/send', [
'headers' => [
'Authorization' => 'Bearer ' . $sendgrid_api_key,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode($emailBody),
]);
if (is_wp_error($mail_response)) {
wp_send_json(['message' => '✅ Added to list, but failed to send admin email.']);
}
wp_send_json(['message' => '✅ Thank you! You are signed up.']);
exit;
}
—
### 📝 Optional (Add Custom Fields)
If you want to store `regular` and `specific` as custom fields in SendGrid:
1. Go to **SendGrid → Marketing → Contacts → Custom Fields**
2. Create two new fields: e.g. `regular_interest`, `specific_topic`
3. Note down their **custom field IDs** (looks like `e1_T`, `e2_T` etc.)
4. Then adjust:
'custom_fields' => [
'e1_T' => $regular,
'e2_T' => $specific
]
—
Let me know if you want to make this form GDPR-compliant or add double opt-in too.
Here’s your **cleaned and adjusted version**:
—
### ✅ Final Code – Only Send Contact to SendGrid List
public function handle_newsletter_form() {
check_ajax_referer('newsletter_nonce', 'nonce');
$email = sanitize_email($_POST['email']);
$regular = sanitize_text_field($_POST['regular']);
$specific = sanitize_text_field($_POST['specific']);
$sendgrid_api_key = 'SG.Et-F1hGBQ924bE-jjmTkccQPGLudM';
$listId = 'f3eaa89e-b043-4652-9b37-7eb45501b7b6';
// ✅ Prepare contact data
$contactData = [
'list_ids' => [$listId],
'contacts' => [[
'email' => $email,
'custom_fields' => [
// If you've created custom fields in SendGrid, add them here using field IDs
// 'e1_T' => $regular,
// 'e2_T' => $specific
]
]]
];
// ✅ Send to SendGrid
$response = wp_remote_post('https://api.sendgrid.com/v3/marketing/contacts', [
'headers' => [
'Authorization' => 'Bearer ' . $sendgrid_api_key,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode($contactData),
]);
if (is_wp_error($response)) {
wp_send_json(['message' => '❌ Failed to subscribe. Please try again later.']);
}
wp_send_json(['message' => '✅ Thank you! You are now subscribed.']);
exit;
}
—
### 🧠 Notes:
– This only **adds the contact** to your SendGrid **Marketing List**.
– `custom_fields` section is optional — add if you have them set up in SendGrid.
– No email will be sent from your server or SendGrid.
—
Let me know if you want:
– custom field IDs added,
– confirmation messages styled,
– or the same logic implemented via REST API for headless setups.
"data": {
"headers": {},
"body": "",
"response": {
"code": 405,
"message": "Method Not Allowed"
},
"cookies": [],
"filename": null,
"http_response": {
"data": null,
"headers": null,
"status": null
}
}
}
abo e the respons ei get
In your case, you're trying to **add a contact** to SendGrid using this endpoint:
https://api.sendgrid.com/v3/marketing/contacts
This endpoint **requires a `PUT` method**, not `POST`.
—
### ✅ Fix
Update your `wp_remote_post()` to use the correct method by changing it to `wp_remote_request()` with `'method' => 'PUT'`.
—
### 🔧 Updated Code:
$response = wp_remote_request('https://api.sendgrid.com/v3/marketing/contacts', [
'method' => 'PUT', // ✅ Correct method
'headers' => [
'Authorization' => 'Bearer ' . $sendgrid_api_key,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode($contactData),
]);
—
### ✅ Full Working Snippet (Corrected)
public function handle_newsletter_form() {
check_ajax_referer('newsletter_nonce', 'nonce');
$email = sanitize_email($_POST['email']);
$regular = sanitize_text_field($_POST['regular']);
$specific = sanitize_text_field($_POST['specific']);
$sendgrid_api_key = 'SG.Et-F1hGBQ924bE-jjmTkccQPGLudM';
$listId = 'f3eaa89e-b043-4652-9b37-7eb45501b7b6';
$contactData = [
'list_ids' => [$listId],
'contacts' => [[
'email' => $email,
'custom_fields' => [
// Uncomment if custom fields set up in SendGrid
// 'e1_T' => $regular,
// 'e2_T' => $specific
]
]]
];
$response = wp_remote_request('https://api.sendgrid.com/v3/marketing/contacts', [
'method' => 'PUT', // ✅ Use PUT instead of POST
'headers' => [
'Authorization' => 'Bearer ' . $sendgrid_api_key,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode($contactData),
]);
if (is_wp_error($response)) {
wp_send_json(['message' => '❌ Failed to subscribe.']);
}
wp_send_json(['message' => '✅ Thank you! You are now subscribed.']);
exit;
}
—
Let me know if you want to:
– handle duplicate emails gracefully,
– tag users,
– or add GDPR compliance.
"data": {
"headers": {},
"body": "{"errors":[{"field":"","message":"invalid input"}]}",
"response": {
"code": 400,
"message": "Bad Request"
},
"cookies": [],
"filename": null,
"http_response": {
"data": null,
"headers": null,
"status": null
}
}
}
400 Bad Request
"message": "invalid input"
This means the payload (`$contactData`) you're sending to the SendGrid API is malformed or invalid.
—
### ✅ Let's Debug It Step-by-Step:
#### 1. ✅ **Check your `$contactData` structure**
Your `$contactData` **must** be a **valid JSON with required fields**, like this:
$contactData = [
'list_ids' => ['f3eaa89e-b043-4652-9b37-7eb45501b7b6'],
'contacts' => [
[
'email' => $email,
// Only include fields if they have valid values
// 'first_name' => 'Neeraj',
// 'custom_fields' => [
// 'e1_T' => $regular,
// 'e2_T' => $specific
// ]
]
]
];
If **`$email` is empty or invalid**, or if **`custom_fields` contain wrong keys**, you'll get `400`.
—
### 🔍 Let's validate:
Here’s a working minimal example:
#### ✅ Final Minimal Working Version:
public function handle_newsletter_form() {
check_ajax_referer('newsletter_nonce', 'nonce');
$email = sanitize_email($_POST['email']);
if (!is_email($email)) {
wp_send_json(['message' => '❌ Invalid email address.']);
}
$sendgrid_api_key = 'SG.Et-F1hGBQ924bE-jjmTkccQPGLudM';
$listId = 'f3eaa89e-b043-4652-9b37-7eb45501b7b6';
$contactData = [
'list_ids' => [$listId],
'contacts' => [
[
'email' => $email
]
]
];
$response = wp_remote_request('https://api.sendgrid.com/v3/marketing/contacts', [
'method' => 'PUT',
'headers' => [
'Authorization' => 'Bearer ' . $sendgrid_api_key,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode($contactData),
]);
if (is_wp_error($response)) {
wp_send_json(['message' => '❌ Failed to connect to SendGrid.']);
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (isset($body['errors'])) {
wp_send_json(['message' => '❌ SendGrid error: ' . $body['errors'][0]['message']]);
}
wp_send_json(['message' => '✅ Thank you! You are now subscribed.']);
exit;
}
—
### 📌 If You Still Get Error
Log the `$contactData` just before the request:
error_log(print_r($contactData, true));
Also, log SendGrid's response:
error_log(wp_remote_retrieve_body($response));
—
### ✅ Common Causes of 400:
| Cause | Fix |
|——|—–|
| Missing or invalid email | Check with `is_email($email)` |
| `list_ids` is empty or invalid | Double-check your list ID |
| `custom_fields` keys are not created in SendGrid | Only use custom field IDs you've created (e.g. `e1_T`) |
| Sending null/empty values | Strip them before sending |
—
Let me know if you want to include custom fields next — but let's get the base working first.
'list_ids' => [$listId],
'contacts' => [[
'email' => $email,
'custom_fields' => [
// If you've created custom fields in SendGrid, add them here using field IDs
// 'e1_T' => $regular,
// 'e2_T' => $specific
]
]]
];
—
### ✅ Fix: Only include `custom_fields` **if values and field IDs exist**
SendGrid requires **valid custom field IDs** like `e1_T`, `e2_T`, etc., and will reject the payload if:
– the keys don't exist in your SendGrid dashboard
– the values are `null` or empty
– the structure is wrong
—
### ✅ Best Practice Code:
$contact = [
'email' => $email
];
// OPTIONAL: Add custom fields if you have them configured in SendGrid
$custom_fields = [];
if (!empty($regular)) {
$custom_fields['e1_T'] = $regular; // replace with your actual custom field ID for 'regular'
}
if (!empty($specific)) {
$custom_fields['e2_T'] = $specific; // replace with your actual custom field ID for 'specific'
}
if (!empty($custom_fields)) {
$contact['custom_fields'] = $custom_fields;
}
$contactData = [
'list_ids' => [$listId],
'contacts' => [$contact]
];
—
### 🔍 Find Your Actual Custom Field IDs:
Go to:
– **SendGrid > Marketing > Contacts > Custom Fields**
– Note the **field IDs** that look like `e1_T`, `e2_T`, etc.
– Use them in the `custom_fields` array as keys
—
### ✅ Final JSON Example (Valid Payload)
{
"list_ids": ["f3eaa89e-b043-4652-9b37-7eb45501b7b6"],
"contacts": [
{
"email": "user@example.com",
"custom_fields": {
"e1_T": "General Updates",
"e2_T": "Health Tips"
}
}
]
}
—
Let me know your actual field IDs if you want help hardcoding them in safely. Once that’s fixed, the `400` will disappear.
—
### ✅ Basic KiyOh API Endpoint to Get Reviews Summary
**Example GET endpoint:**
https://www.kiyoh.com/v1/publication/review/external/location?locationId=YOUR_LOCATION_ID
> 🟡 Replace `YOUR_LOCATION_ID` with your actual KiyOh company/location ID (you can find this in your KiyOh account or embedded widget snippet).
—
### 🔍 Example Response:
{
"totalReviews": 234,
"averageRating": 8.9,
"locationId": 12345,
...
}
– **`totalReviews`**: 👈 This is what you’re looking for — total number of reviews.
– **`averageRating`**: Overall rating.
– Other fields include breakdown by date, sentiment, etc.
—
### ✅ Sample Code (PHP + WordPress style)
$locationId = 'YOUR_LOCATION_ID';
$response = wp_remote_get("https://www.kiyoh.com/v1/publication/review/external/location?locationId={$locationId}");
if (is_wp_error($response)) {
return 'Error fetching KiyOh data.';
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (isset($body['totalReviews'])) {
echo 'Total Reviews: ' . $body['totalReviews'];
} else {
echo 'No review data available.';
}
—
### 🔐 Do You Need an API Key?
– For **public summary data**, **no authentication is needed** (i.e., total reviews and rating).
– For **detailed review data or posting reviews**, you need **API credentials (API key + location ID)** — available to paid customers.
—
### ✅ Example Output
Total Reviews: 172
—
Let me know if:
– You want to **display this on your WordPress site with a shortcode**
– Or fetch **individual review data (with date, stars, etc.)**
– Or use **cURL, jQuery, or REST API** instead of `wp_remote_get`