How to Integrate JustEmails With CRM Contact Forms via the API
Wire CRM forms to trigger instant emails via API — confirmation to leads, alerts to your team.
By JustEmails Platform Team
The lead came in at 4:17 PM on a Thursday. HubSpot form, basic info — name, company, email, what they needed. My CRM logged it. I didn't see it until Monday morning because I was traveling and my phone died somewhere over Kansas.
By Monday? Already signed with someone else. Four days cold.
That's when I wired up automatic confirmation emails. Look, it's not rocket science — lead submits a form, they get an instant thank-you with next steps, my team gets an internal alert. No one waits for someone to check their inbox. (I'm still a little bitter about that Kansas phone situation, honestly.)
We're the JustEmails team — part of Velocity Digital Labs, the same folks behind ClickzProtect for click fraud protection, JustAnalytics for privacy-first analytics, and VeloCalls for AI-powered call tracking. This tutorial shows you how to connect your CRM contact forms to the JustEmails API so form submissions trigger real emails — confirmation to the lead, notification to your team — without waiting for anyone to manually respond.
The approach works with HubSpot, Pipedrive, Zoho CRM, Freshsales, and basically any CRM that supports webhooks. If yours doesn't, you can bridge through Zapier or Make.
What You'll Build
A working integration where:
- Someone fills out your CRM's contact form
- They instantly receive a confirmation email from your domain (not some generic
noreply@your-crm.com) - Your sales team gets an internal notification with the lead's details
- Both emails authenticate with SPF/DKIM because they're sent through your verified domain
No more leads sitting unacknowledged. No more refreshing the CRM dashboard like it's Twitter in 2012.
Prerequisites
Before starting:
- A JustEmails account with at least one verified domain — start the 7-day trial if you don't have one
- Your JustEmails API key (Dashboard → API → Keys)
- A CRM with webhook capabilities (HubSpot, Pipedrive, Zoho CRM, etc.) or a Zapier/Make account to bridge
- Somewhere to host a webhook endpoint — Vercel, Cloudflare Workers, AWS Lambda, or even a basic Node.js server all work
- Basic familiarity with HTTP requests and JSON
On sending limits: the base $49/year plan includes 1,000 transactional API emails per month. Two emails per form submission means 500 forms before you'd need more. If you're processing more leads than that — good problem to have — add 10,000 emails/month for $25/year. I'll admit the math here annoyed me at first, but 500 leads/month is actually a lot for most small teams.
Step 1: Create Your JustEmails API Key
Log into JustEmails. Navigate to Dashboard → API → Keys. Click "Create New Key."
Name it something you'll recognize later — "CRM Contact Forms" or "Lead Notifications" works. The key appears once. Copy it immediately.
The format looks like je_live_xxxxxxxxxxxxxxxxxxxx. The live prefix means production sends. Test keys (je_test_) validate your payloads without actually delivering emails — useful for debugging.
Store the key securely. Password manager, environment variable, whatever your setup supports. Don't commit it to a public repo. I've seen devs do this. Three times in the past year alone. The cleanup conversations are never fun.
Step 2: Verify Your Sending Domain
If you haven't already verified your domain in JustEmails, do that now. The API won't send from unverified domains — you'll get a 422 error.
Dashboard → Domains → Add Domain.
JustEmails provides the DNS records you need: SPF, DKIM, DMARC. Add them at your DNS provider — Cloudflare, Route 53, GoDaddy, wherever your domain lives. DNS propagation usually takes 5-30 minutes, though I've watched it take four hours on one particularly stubborn registrar. (You know who you are.)
Green checkmark next to the domain? You're good. The API will accept sends from that domain.
Step 3: Build Your Webhook Handler
This is the code that receives form submissions and calls the JustEmails API. I'm using a Node.js example, but the logic translates to any language.
// webhook-handler.js (for Express, Vercel, or similar)
const JUSTEMAILS_API_KEY = process.env.JUSTEMAILS_API_KEY
const FROM_EMAIL = 'hello@yourdomain.com'
const FROM_NAME = 'Your Company'
const TEAM_EMAIL = 'sales@yourdomain.com'
async function sendEmail(payload) {
const response = await fetch('https://api.justemails.app/v1/email/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${JUSTEMAILS_API_KEY}`,
},
body: JSON.stringify(payload),
})
const data = await response.json()
if (!response.ok) {
throw new Error(data.message || `HTTP ${response.status}`)
}
return data
}
export async function handleFormSubmission(formData) {
const { name, email, company, message } = formData
// Send confirmation to the lead
const confirmationEmail = {
to: email,
from: FROM_EMAIL,
fromName: FROM_NAME,
subject: `Thanks for reaching out, ${name}!`,
html: `
<div style="font-family: -apple-system, sans-serif; max-width: 600px;">
<h2>We received your message</h2>
<p>Hi ${name},</p>
<p>Thanks for contacting us. Someone from our team will get back to you within 24 hours — usually much faster.</p>
<p>Here's what you submitted:</p>
<blockquote style="background: #f5f5f5; padding: 15px; border-left: 4px solid #333;">
${message}
</blockquote>
<p>Talk soon,<br>The Team</p>
</div>
`,
text: `Hi ${name}, thanks for reaching out. We received your message and will reply within 24 hours.`,
}
// Send internal notification to your team
const notificationEmail = {
to: TEAM_EMAIL,
from: FROM_EMAIL,
fromName: 'Lead Alert',
subject: `New lead: ${name} from ${company || 'Unknown Company'}`,
html: `
<div style="font-family: -apple-system, sans-serif; max-width: 600px;">
<h2>New Contact Form Submission</h2>
<table style="width: 100%; border-collapse: collapse;">
<tr>
<td style="padding: 8px; border-bottom: 1px solid #eee;"><strong>Name:</strong></td>
<td style="padding: 8px; border-bottom: 1px solid #eee;">${name}</td>
</tr>
<tr>
<td style="padding: 8px; border-bottom: 1px solid #eee;"><strong>Email:</strong></td>
<td style="padding: 8px; border-bottom: 1px solid #eee;"><a href="mailto:${email}">${email}</a></td>
</tr>
<tr>
<td style="padding: 8px; border-bottom: 1px solid #eee;"><strong>Company:</strong></td>
<td style="padding: 8px; border-bottom: 1px solid #eee;">${company || 'Not provided'}</td>
</tr>
</table>
<h3>Message:</h3>
<p style="background: #f5f5f5; padding: 15px;">${message}</p>
<p><a href="https://your-crm.com/contacts?email=${encodeURIComponent(email)}" style="background: #0070f3; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">View in CRM →</a></p>
</div>
`,
text: `New lead: ${name} (${email}) from ${company || 'Unknown'}. Message: ${message}`,
}
// Fire both emails
const [confirmationResult, notificationResult] = await Promise.all([
sendEmail(confirmationEmail),
sendEmail(notificationEmail),
])
return {
confirmation: confirmationResult,
notification: notificationResult,
}
}
Two emails, one form submission. The Promise.all fires both in parallel so the response is fast.
I used to send these sequentially. Added 300-400ms of latency for absolutely no reason. Felt dumb when I realized. Parallel is better.
Step 4: Create the Webhook Endpoint
Wrap the handler in an HTTP endpoint. Here's an Express version, but Vercel/Cloudflare Workers/whatever works similarly:
// server.js
import express from 'express'
import { handleFormSubmission } from './webhook-handler.js'
const app = express()
app.use(express.json())
app.post('/webhook/contact-form', async (req, res) => {
try {
// Validate required fields
const { name, email, message } = req.body
if (!name || !email || !message) {
return res.status(400).json({
success: false,
error: 'Missing required fields: name, email, message',
})
}
// Basic email format check
if (!email.includes('@') || !email.includes('.')) {
return res.status(400).json({
success: false,
error: 'Invalid email format',
})
}
const result = await handleFormSubmission(req.body)
res.json({ success: true, result })
} catch (error) {
console.error('Webhook error:', error)
res.status(500).json({
success: false,
error: 'Failed to process form submission',
})
}
})
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`Webhook server running on port ${PORT}`)
})
Deploy this wherever you host serverless functions. Note the URL — you'll need it for the CRM configuration.
Step 5: Configure Your CRM's Webhook
Every CRM handles this differently. Here's the general pattern:
HubSpot:
Go to Settings → Integrations → Webhooks → Create Webhook. Set the target URL to your endpoint (like https://your-server.com/webhook/contact-form). Map the form fields to the JSON payload.
Pipedrive: Go to Tools → Webhooks → Create new webhook. Select "Activity" or "Deal" events depending on your setup. Point to your endpoint.
Zoho CRM: Settings → Developer Space → Functions → Create Function. Use deluge scripting to call your webhook when a lead is created.
No native webhook support? Route through Zapier. Trigger on new CRM contact, action is Webhooks by Zapier (POST). Same endpoint, same JSON structure. Adds a small delay (usually under 2 seconds), but it works. Not my favorite approach — I think webhooks should be native in every CRM by now, it's 2026 — but sometimes you work with what you've got.
If you're already using Zapier for other automations, check our Zapier integration tutorial for the visual setup. For teams managing email across multiple domains, our multi-domain email management guide covers unified inbox strategies.
Step 6: Test the Integration
Submit a test form. Use your own email address.
Watch for:
- Confirmation email arrives in your inbox (check spam if needed)
- Team notification arrives at your team email address
- Both emails show your domain in the "from" field, not some third-party sender
- Response time is fast — under 5 seconds from submission to inbox
If emails land in spam, check your domain's authentication. Run it through mail-tester.com. JustEmails auto-configures SPF/DKIM, but DNS issues can still cause problems. Our custom domain email setup guide covers the troubleshooting. For a deep dive on authentication protocols, see our SPF DKIM DMARC explained guide.
Common Errors and How to Fix Them
401 Unauthorized
Your API key is wrong. Check:
- Did you copy the full key including the
je_live_prefix? - Is the
Authorizationheader formatted asBearer <key>with a space after Bearer? - Did you accidentally use a test key (
je_test_) when you meant production?
I've debugged this for other devs more times than I'd like to admit. It's almost always a missing space or incomplete key. Every. Single. Time.
422 Unprocessable Entity: from address not verified
The domain in your from field isn't verified in JustEmails. Check Dashboard → Domains. If it's not there, add it. If it's pending, the DNS records haven't propagated yet — give it more time or check for typos in the records.
Webhook times out
Your CRM expects a response within a few seconds. If the email sending takes too long, the webhook might timeout even though the emails eventually send. Solutions:
- Return a 200 immediately and process emails asynchronously
- Use a queue (SQS, Redis, whatever) to decouple receipt from processing
- Most CRMs retry on timeout, so the second attempt usually works
Emails send but formatting is broken
HTML email rendering is cursed. Absolutely cursed. Gmail, Outlook, and Apple Mail all interpret CSS differently, and don't even get me started on Outlook's Word-based rendering engine — yes, that's still a thing in 2026. Stick to inline styles, tables for layout, and test across clients. JustBrowser can help if you need to preview renders without switching between email accounts.
Duplicate emails
Your CRM might fire the webhook multiple times — on form submit AND on contact creation, for example. Add idempotency: track processed form IDs and skip duplicates. Or configure your CRM to fire only on one event, not multiple.
Customizing the Email Templates
The basic templates work. They're also generic. Here's how to make them better:
Add context to confirmations:
<p>Based on what you shared, here are some resources while you wait:</p>
<ul>
<li><a href="https://yourdomain.com/pricing">Our pricing</a></li>
<li><a href="https://yourdomain.com/case-studies">Client case studies</a></li>
</ul>
Add urgency to team notifications:
<p style="background: #fff3cd; padding: 10px; border-radius: 4px;">
⚡ Lead requested same-day response
</p>
Include lead scoring signals:
<p><strong>Company size:</strong> ${employees || 'Unknown'}</p>
<p><strong>Budget range:</strong> ${budget || 'Not specified'}</p>
The more useful your internal notification, the faster your team can prioritize. A lead asking about enterprise pricing probably warrants a faster callback than someone browsing your blog. (Although, honestly? Some of our best customers came from blog readers. Hard to predict.)
Next Steps
You've got the basic integration running. Here's what to consider:
Add lead source tracking. Pass UTM parameters from your form into the email payload. Your team notifications can show where leads came from — paid search, organic, referral. If you're running paid campaigns, ClickzProtect catches fraudulent clicks before they pollute your lead data. Track your overall marketing performance with JustAnalytics — no cookie banners required.
Build email templates for different forms. A demo request deserves a different confirmation than a general inquiry. Route based on form ID or a hidden field.
Set up webhook logging. Log every request and response somewhere (database, log aggregator, even a simple JSON file). When something breaks at 2 AM, you'll want to see what payload caused it.
Monitor your transactional email usage. Dashboard → API → Usage shows your monthly count. 1,000 emails/month included, two emails per submission, 500 forms before you need the add-on. If you're tracking engagement beyond email, JustAnalytics handles website analytics without the Google dependency.
The goal? Leads never sit unacknowledged. They submit, they get instant confirmation, your team gets instant alert. No manual checking, no missed opportunities, no more losing deals because you checked your CRM on Monday instead of Thursday.
That Kansas phone situation still bugs me.
Frequently Asked Questions
Can I send both a confirmation to the lead AND an internal notification from one form submission?
Yes. Your webhook handler makes two separate POST calls to the JustEmails API — one for the thank-you email to the lead, one for the internal alert to your sales team. Both calls use the same API key and endpoint. Just construct two different payloads and fire them sequentially or in parallel. Most CRMs even let you configure multiple webhook URLs per form, so you could split the logic across separate serverless functions if you prefer.
How do I test the integration without spamming real leads?
Use a test email address you control and submit the form yourself. JustEmails also provides test API keys with the je_test_ prefix that validate payloads without actually sending. For production debugging, check Dashboard → API → Logs to see request/response details for recent sends.
What CRMs support webhooks for contact form submissions?
Most modern CRMs support webhooks natively or through add-ons. HubSpot, Pipedrive, Zoho CRM, Close, Freshsales, and Monday CRM all offer webhook triggers on form submissions or new contact creation. Salesforce requires Flow Builder or a third-party tool like Zapier. If your CRM lacks native webhooks, route through Zapier, Make, or n8n as a bridge.
Do I need to verify my sending domain before the integration works?
Yes. JustEmails requires domain verification before sending. Add your domain in Dashboard → Domains, then add the DNS records JustEmails provides. Without verification, API calls return a 422 error. This isn't a limitation — it protects your deliverability by ensuring proper SPF/DKIM authentication on every send.
Try JustEmails
$49/year flat — unlimited domains, unlimited mailboxes, 10 GB storage, full IMAP/SMTP. Built for agencies, freelancers, and anyone managing email across more than one domain.