Skip to main content

Webflow Forms Integration with SendSquared

Overview

This comprehensive guide shows you how to integrate Webflow forms with SendSquared's API for seamless lead capture, contact management, and marketing automation. We'll cover multiple integration methods, from simple popup forms to advanced Lead API integrations with custom field mapping.

Why Integrate Webflow Forms with SendSquared?

  • Automatic Lead Capture: Every form submission creates a contact in SendSquared
  • Real-time Sync: Instant data transfer without manual exports
  • Marketing Automation: Trigger campaigns based on form submissions
  • Custom Field Mapping: Pass any Webflow form data to SendSquared
  • Multi-form Support: Handle different forms for different purposes
  • Advanced Tracking: Track source, campaign, and user behavior

Prerequisites

Before starting, ensure you have:

  • SendSquared account with API access
  • Webflow account (any plan with custom code access)
  • Basic understanding of HTML and JavaScript
  • Published Webflow site with SSL (https://)

Integration Methods

Method 1: Simple Popup Form Integration

Best for: Newsletter signups, basic lead capture

// Add to Webflow Page Settings > Custom Code > Before closing body tag
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('#newsletter-form');

if (form) {
form.addEventListener('submit', function(e) {
e.preventDefault();

const formData = new FormData(form);
const email = formData.get('email');
const name = formData.get('name');

// Your SendSquared List Group UUID
const token = 'YOUR_LIST_GROUP_UUID';
const apiUrl = `https://app-api.sendsquared.com/v1/pub/popup?token=${token}`;

fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: email,
name: name
})
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Success:', data);
// Show success message
document.querySelector('.w-form-done').style.display = 'block';
document.querySelector('.w-form-fail').style.display = 'none';
form.reset();
})
.catch(error => {
console.error('Error:', error);
// Show error message
document.querySelector('.w-form-done').style.display = 'none';
document.querySelector('.w-form-fail').style.display = 'block';
});
});
}
});
</script>

Method 2: Advanced Lead API Integration

Best for: Contact forms, multi-field forms, CRM integration

// Add to Webflow Page Settings > Custom Code > Before closing body tag
<script>
document.addEventListener('DOMContentLoaded', function() {
const contactForm = document.querySelector('#contact-form');

if (contactForm) {
contactForm.addEventListener('submit', async function(e) {
e.preventDefault();

const formData = new FormData(contactForm);

// Format phone to E164 if provided
function formatToE164(phone) {
if (!phone) return '';
const cleaned = phone.replace(/\D/g, '');
if (cleaned.length === 10) {
return '+1' + cleaned;
} else if (cleaned.length === 11 && cleaned[0] === '1') {
return '+' + cleaned;
}
return phone;
}

// Prepare the data
const leadData = {
first_name: formData.get('first-name') || '',
last_name: formData.get('last-name') || '',
email: formData.get('email'),
mobile_phone: formatToE164(formData.get('phone')),
company: formData.get('company') || '',
message: formData.get('message') || '',
source: 'Webflow Contact Form',
tags: ['webflow', 'contact-form'],
custom_fields: {
form_name: 'Contact Form',
submission_date: new Date().toISOString(),
page_url: window.location.href,
referrer: document.referrer || 'Direct'
}
};

// Your SendSquared API Key
const apiKey = 'YOUR_API_KEY';
const apiUrl = 'https://app-api.sendsquared.com/v1/public/lead-with-contact';

try {
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey
},
body: JSON.stringify(leadData)
});

const result = await response.json();

if (response.ok) {
console.log('Lead created successfully:', result);

// Show Webflow success message
contactForm.style.display = 'none';
contactForm.nextElementSibling.style.display = 'block';

// Track conversion
if (typeof gtag !== 'undefined') {
gtag('event', 'form_submission', {
'event_category': 'engagement',
'event_label': 'contact_form'
});
}

// Optional: Redirect after success
// setTimeout(() => {
// window.location.href = '/thank-you';
// }, 2000);
} else {
throw new Error(result.message || 'Submission failed');
}
} catch (error) {
console.error('Submission error:', error);

// Show Webflow error message
const errorDiv = contactForm.parentElement.querySelector('.w-form-fail');
if (errorDiv) {
errorDiv.style.display = 'block';
errorDiv.textContent = 'There was an error submitting the form. Please try again.';
}
}
});
}
});
</script>

Step-by-Step Implementation Guide

Step 1: Create Your Form in Webflow

  1. Open your Webflow project in Designer
  2. Add a Form Block from the Add panel
  3. Set the form ID (e.g., contact-form)
  4. Add your form fields with proper names:
    • Email field: name="email" (required)
    • First Name: name="first-name"
    • Last Name: name="last-name"
    • Phone: name="phone"
    • Company: name="company"
    • Message: name="message"

Step 2: Configure Form Settings

  1. Select the form element
  2. In Settings panel, set:
    • Form Name: "Contact Form"
    • Method: POST (will be overridden by JavaScript)
    • Action: "#" (will be prevented by JavaScript)

Step 3: Get Your SendSquared Credentials

  1. Log into SendSquared Dashboard
  2. Navigate to Settings > API Keys
  3. Create or copy your API key
  4. For popup forms: Go to Lists > Your List > Settings and copy the List Group UUID

Step 4: Add the Integration Code

  1. Go to Pages in Webflow
  2. Click the gear icon next to your page
  3. Scroll to Custom Code
  4. Add the JavaScript code in the "Before </body> tag" section
  5. Replace placeholders:
    • YOUR_API_KEY with your actual API key
    • YOUR_LIST_GROUP_UUID with your list UUID
    • Form IDs to match your forms

Step 5: Add Error Handling UI

Add these elements to your form for better UX:

<!-- Custom success message (optional) -->
<div class="success-message" style="display: none;">
<h3>Thank you!</h3>
<p>We've received your submission and will be in touch soon.</p>
</div>

<!-- Custom error message (optional) -->
<div class="error-message" style="display: none;">
<p>There was an error. Please try again or contact us directly.</p>
</div>

Step 6: Publish and Test

  1. Save your changes in Webflow
  2. Publish your site
  3. Test the form with real data
  4. Check SendSquared dashboard for new contacts

Advanced Features

Multiple Forms on Same Page

// Handle multiple forms with different purposes
document.addEventListener('DOMContentLoaded', function() {
// Newsletter form
const newsletterForm = document.querySelector('#newsletter-form');
if (newsletterForm) {
newsletterForm.addEventListener('submit', function(e) {
e.preventDefault();
handleFormSubmission(this, 'newsletter');
});
}

// Contact form
const contactForm = document.querySelector('#contact-form');
if (contactForm) {
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
handleFormSubmission(this, 'contact');
});
}

// Quote request form
const quoteForm = document.querySelector('#quote-form');
if (quoteForm) {
quoteForm.addEventListener('submit', function(e) {
e.preventDefault();
handleFormSubmission(this, 'quote');
});
}

async function handleFormSubmission(form, formType) {
const formData = new FormData(form);

// Customize data based on form type
const leadData = {
email: formData.get('email'),
first_name: formData.get('first-name') || '',
last_name: formData.get('last-name') || '',
tags: [formType, 'webflow'],
source: `Webflow ${formType} Form`,
custom_fields: {
form_type: formType,
submission_date: new Date().toISOString()
}
};

// Add form-specific fields
if (formType === 'quote') {
leadData.custom_fields.service_interest = formData.get('service');
leadData.custom_fields.budget = formData.get('budget');
}

// Send to SendSquared
try {
const response = await fetch('https://app-api.sendsquared.com/v1/public/lead-with-contact', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'YOUR_API_KEY'
},
body: JSON.stringify(leadData)
});

if (response.ok) {
// Show success for this specific form
form.style.display = 'none';
form.nextElementSibling.style.display = 'block';
} else {
throw new Error('Submission failed');
}
} catch (error) {
console.error(`Error submitting ${formType} form:`, error);
// Show error message
const errorDiv = form.parentElement.querySelector('.w-form-fail');
if (errorDiv) {
errorDiv.style.display = 'block';
}
}
}
});
</script>

Field Validation

// Add custom validation before submission
function validateForm(formData) {
const email = formData.get('email');
const phone = formData.get('phone');

// Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showError('Please enter a valid email address');
return false;
}

// Phone validation (if provided)
if (phone) {
const phoneRegex = /^[\d\s\-\+\(\)]+$/;
if (!phoneRegex.test(phone) || phone.replace(/\D/g, '').length < 10) {
showError('Please enter a valid phone number');
return false;
}
}

return true;
}

function showError(message) {
const errorDiv = document.querySelector('.custom-error');
if (errorDiv) {
errorDiv.textContent = message;
errorDiv.style.display = 'block';
setTimeout(() => {
errorDiv.style.display = 'none';
}, 5000);
}
}

UTM Parameter Tracking

// Capture UTM parameters for attribution
function getUTMParameters() {
const params = new URLSearchParams(window.location.search);
return {
utm_source: params.get('utm_source') || '',
utm_medium: params.get('utm_medium') || '',
utm_campaign: params.get('utm_campaign') || '',
utm_term: params.get('utm_term') || '',
utm_content: params.get('utm_content') || ''
};
}

// Include in your lead data
const leadData = {
// ... other fields
custom_fields: {
...getUTMParameters(),
form_name: 'Contact Form',
submission_date: new Date().toISOString()
}
};

Progressive Form Enhancement

// Show/hide fields based on user selection
document.addEventListener('DOMContentLoaded', function() {
const serviceSelect = document.querySelector('#service-type');
const additionalFields = document.querySelector('.additional-fields');

if (serviceSelect && additionalFields) {
serviceSelect.addEventListener('change', function() {
if (this.value === 'custom') {
additionalFields.style.display = 'block';
} else {
additionalFields.style.display = 'none';
}
});
}
});

Troubleshooting

Common Issues and Solutions

Form Not Submitting

Symptoms: Nothing happens when clicking submit button

Solutions:

  1. Check browser console for JavaScript errors
  2. Verify form ID matches in your code
  3. Ensure JavaScript is in the correct location (before </body>)
  4. Check if Webflow's jQuery conflicts with your code
// Use this wrapper to avoid conflicts
(function() {
// Your code here
})();

API Key Errors

Error: "Invalid API key" or 401 Unauthorized

Solutions:

  1. Verify API key is correct and active
  2. Check for extra spaces in the API key
  3. Ensure you're using the correct header name (x-api-key)

CORS Errors

Error: "Access to fetch at... from origin... has been blocked by CORS"

Solutions:

  1. Ensure your domain is whitelisted in SendSquared
  2. Use the correct API endpoint
  3. Check if you're testing locally (use published site)

Data Not Appearing in SendSquared

Symptoms: Form submits successfully but no data in SendSquared

Solutions:

  1. Check API response in browser console
  2. Verify field names match exactly
  3. Ensure required fields are included
  4. Check SendSquared dashboard filters

Debugging Tips

// Add detailed logging for troubleshooting
console.log('Form Data:', Object.fromEntries(formData));
console.log('API Request:', leadData);

// Log the full response
.then(response => {
console.log('Response status:', response.status);
console.log('Response headers:', response.headers);
return response.json();
})
.then(data => {
console.log('Response data:', data);
});

Performance Optimization

Lazy Loading the Integration

// Only load integration when form is visible
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadFormIntegration();
observer.unobserve(entry.target);
}
});
});

const form = document.querySelector('#contact-form');
if (form) {
observer.observe(form);
}

function loadFormIntegration() {
// Your form integration code here
}

Debouncing Form Validation

// Prevent excessive validation calls
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}

const validateEmail = debounce((email) => {
// Validation logic
}, 500);

document.querySelector('#email').addEventListener('input', (e) => {
validateEmail(e.target.value);
});

Security Considerations

Protecting Your API Key

Security Best Practice

Never expose sensitive API keys in client-side code for production environments. Consider using a serverless function or backend proxy for enhanced security.

Recommended Approach: Use Webflow Logic or a serverless function:

// Instead of direct API call, use a proxy endpoint
fetch('https://your-proxy.com/submit-form', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
})

Input Sanitization

// Sanitize user input to prevent XSS
function sanitizeInput(input) {
const div = document.createElement('div');
div.textContent = input;
return div.innerHTML;
}

// Apply to all text inputs
const sanitizedData = {
name: sanitizeInput(formData.get('name')),
message: sanitizeInput(formData.get('message'))
};

Frequently Asked Questions

Can I integrate multiple Webflow forms with different SendSquared lists?

Yes! You can integrate multiple forms and route them to different lists or campaigns. Use different API endpoints or list group UUIDs for each form, and customize the tags and custom fields to differentiate submissions.

How do I test the integration without affecting live data?

  1. Create a test list in SendSquared
  2. Use the test list's UUID during development
  3. Submit test data with obvious test names (e.g., "Test User")
  4. Switch to production credentials when ready

What happens if SendSquared API is down?

Implement a fallback mechanism:

.catch(error => {
console.error('SendSquared API error:', error);

// Fallback: Store locally and retry later
const failedSubmission = {
data: leadData,
timestamp: new Date().toISOString()
};

localStorage.setItem('failed_submission', JSON.stringify(failedSubmission));

// Still show success to user
showSuccessMessage('Thank you! We\'ll process your submission shortly.');

// Retry later
setTimeout(retryFailedSubmissions, 60000);
});

Can I integrate with Webflow's native form handling too?

Yes, you can use both. First let Webflow handle the form, then send to SendSquared:

// After Webflow's success message appears
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.target.classList.contains('w-form-done') &&
mutation.target.style.display === 'block') {
// Send to SendSquared after Webflow processes
sendToSendSquared();
}
});
});

observer.observe(document.querySelector('.w-form-done'), {
attributes: true,
attributeFilter: ['style']
});

How do I handle file uploads?

Webflow forms don't natively support file uploads to external services. Options:

  1. Use a third-party service like Uploadcare
  2. Convert files to base64 (small files only)
  3. Use a two-step process with file upload to cloud storage

What's the rate limit for the SendSquared API?

SendSquared API typically allows:

  • 1000 requests per hour per API key
  • 10 concurrent requests

Implement rate limiting in your code:

let requestCount = 0;
const maxRequests = 10;

async function rateLimitedSubmit(data) {
if (requestCount >= maxRequests) {
await new Promise(resolve => setTimeout(resolve, 1000));
}

requestCount++;
try {
const response = await fetch(apiUrl, options);
return response;
} finally {
requestCount--;
}
}

Can I update existing contacts instead of creating new ones?

Yes, use the Lead API with email as the unique identifier. SendSquared will update existing contacts with matching emails rather than creating duplicates.

How do I track conversions in Google Analytics?

Add tracking code after successful submission:

.then(data => {
// Google Analytics 4
if (typeof gtag !== 'undefined') {
gtag('event', 'generate_lead', {
'currency': 'USD',
'value': 100
});
}

// Facebook Pixel
if (typeof fbq !== 'undefined') {
fbq('track', 'Lead');
}
});

Support

Need help with your integration? Contact our support team: