Webform & hooks

Stood CRM Web Form

Overview

The Stood CRM Web Form allows you to embed a contact/lead capture form on any website. Form submissions automatically create:

All entities are properly linked together, with smart duplicate handling for existing contacts.


Quick Start

1. Get Your Credentials

You need two pieces of information from your Stood CRM team:

2. Copy & Customize the Form

Use the complete HTML example below. Update the CONFIG section with your credentials:

const CONFIG = {
teamId: 'YOUR_TEAM_ID',
teamKey: 'YOUR_TEAM_KEY',
endpoint: 'https://BASE_URL.cloudfunctions.net/webFormSubmit'
};

3. Deploy Anywhere

Save the HTML file and host it on any web server, or embed it in your existing website.


Form Fields

Standard Fields (Mapped to CRM Objects)

Use dot notation to specify which CRM object the field belongs to:

Contact Fields:

Account Fields:

Deal Fields:

Tracking:

Custom Fields

Any additional fields are automatically stored as custom fields:

Just use the format: objectType.fieldName in your form inputs.


URL Parameters for Pre-filling

You can pre-fill form fields via URL parameters:

https://yoursite.com/contact?partner_key=ABC123&partner_email=partner@example.com&source=LinkedIn

The form automatically:


How It Works (Behind the Scenes)

When a form is submitted:

  1. Validation: Team key is verified and rate limiting is applied

  2. Account Creation:

    • Finds existing account by name, or creates new one

    • If no company name provided, uses contact's last name

  3. Contact Creation:

    • Checks if contact exists by email/phone

    • Creates new contact or updates existing one

    • If contact exists with different account, preserves their original account link

  4. Deal Creation:

    • Always creates a new deal for each submission

    • Default deal name: AccountName - sourceName (or LastName - sourceName)

    • Default stage: s0

    • Default closing date: 3 months from today

    • Automatically links to account and adds contact to relatedContacts

    • If contact already existed, adds note to deal description


Security & Rate Limiting


Complete HTML Example

Below is a fully functional form ready to use:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stood CRM - Contact Form</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
max-width: 500px;
margin: 50px auto;
padding: 20px;
background: #f5f5f5;
}
form {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h2 {
margin-top: 0;
}
input, textarea {
width: 100%;
padding: 10px;
margin: 8px 0;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
textarea {
min-height: 80px;
font-family: inherit;
resize: vertical;
}
input:read-only {
background: #f0f0f0;
color: #666;
cursor: not-allowed;
}
.partner-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #e0e0e0;
}
.section-title {
font-size: 14px;
font-weight: 600;
color: #666;
margin-bottom: 10px;
}
button {
width: 100%;
padding: 12px;
background: #4f46e5;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
margin-top: 10px;
}
button:hover {
background: #4338ca;
}
button:disabled {
background: #9ca3af;
cursor: not-allowed;
}
.message {
padding: 12px;
margin: 10px 0;
border-radius: 4px;
}
.success {
background: #d1fae5;
color: #065f46;
}
.error {
background: #fee2e2;
color: #991b1b;
}
.dashboard-section {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
text-align: center;
margin-top: 20px;
}
.dashboard-btn {
display: inline-block;
padding: 12px 24px;
background: #6b7280;
color: white;
text-decoration: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
}
.dashboard-btn:hover {
background: #4b5563;
}
</style>
</head>
<body>
<form id="contactForm">
<h2>Contact Us</h2>
<div id="message"></div>
<input type="text" name="contact.firstName" placeholder="First Name *" required>
<input type="text" name="contact.lastName" placeholder="Last Name *" required>
<input type="email" name="contact.email" placeholder="Email *" required>
<input type="tel" name="contact.phone" placeholder="Phone">
<input type="text" name="account.name" placeholder="Company">
<textarea name="deal.description" placeholder="Message (optional)"></textarea>
<!-- Partner/Referral Section (hidden by default, shown when URL has partner params) -->
<div class="partner-section" id="partnerSection" style="display: none;">
<div class="section-title">Partner Information</div>
<input type="text" name="deal.partnerKey" id="partnerKey" placeholder="Partner Key">
<input type="email" name="deal.partnerEmail" id="partnerEmail" placeholder="Partner Email (optional)">
</div>
<!-- Hidden tracking fields -->
<input type="hidden" name="sourceName" value="Website">
<button type="submit" id="submitBtn">Submit</button>
</form>
<div class="dashboard-section">
<a href="" class="dashboard-btn">Access Dashboard</a>
</div>
<script>
// ==========================================
// CONFIGURATION - UPDATE THESE VALUES
// ==========================================
const CONFIG = {
teamId: 'YOUR_TEAM_ID',
teamKey: 'YOUR_TEAM_KEY',
endpoint: 'https://BASE_URL.cloudfunctions.net/webFormSubmit'
};
// ==========================================
// FORM HANDLING CODE
// ==========================================
const form = document.getElementById('contactForm');
const submitBtn = document.getElementById('submitBtn');
const messageDiv = document.getElementById('message');
form.addEventListener('submit', async (e) => {
e.preventDefault();
// Disable form during submission
submitBtn.disabled = true;
submitBtn.textContent = 'Submitting...';
messageDiv.innerHTML = '';
// Collect form data
const formData = {};
const inputs = form.querySelectorAll('input[name], textarea[name]');
inputs.forEach(input => {
if (input.value.trim()) {
formData[input.name] = input.value.trim();
}
});
try {
// Submit to Stood CRM
const response = await fetch(CONFIG.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
teamId: CONFIG.teamId,
teamKey: CONFIG.teamKey,
formData: formData
})
});
const result = await response.json();
if (result.success) {
// Success!
messageDiv.innerHTML = '<div class="message success">✓ Thank you! We\'ll be in touch soon.</div>';
form.reset();
// Optional: Redirect after success
// setTimeout(() => window.location.href = '/thank-you', 2000);
} else {
// Error from server
messageDiv.innerHTML = `<div class="message error">✗ ${result.error || 'Submission failed. Please try again.'}</div>`;
}
} catch (error) {
// Network or other error
console.error('Submission error:', error);
messageDiv.innerHTML = '<div class="message error">✗ Network error. Please check your connection and try again.</div>';
} finally {
// Re-enable form
submitBtn.disabled = false;
submitBtn.textContent = 'Submit';
}
});
// Optional: Auto-fill partner info from URL
// Example: yoursite.com/contact?partner_key=ABC123&partner_email=partner@example.com&source=LinkedIn
const params = new URLSearchParams(window.location.search);
const partnerSection = document.getElementById('partnerSection');
const partnerKeyInput = document.getElementById('partnerKey');
const partnerEmailInput = document.getElementById('partnerEmail');
let hasPartnerInfo = false;
if (params.has('partner_key')) {
partnerKeyInput.value = params.get('partner_key');
partnerKeyInput.readOnly = true;
hasPartnerInfo = true;
}
if (params.has('partner_email')) {
partnerEmailInput.value = params.get('partner_email');
partnerEmailInput.readOnly = true;
hasPartnerInfo = true;
}
if (params.has('source')) {
document.querySelector('[name="sourceName"]').value = params.get('source');
}
// Show partner section if any partner info is present
if (hasPartnerInfo) {
partnerSection.style.display = 'block';
}
</script>
</body>
</html>

Customization Tips

  1. Styling: Modify the <style> section to match your brand

  2. Fields: Add/remove fields as needed using the objectType.fieldName pattern

  3. Success Message: Customize the success message or add a redirect

  4. Validation: Add client-side validation for better UX

  5. Analytics: Add tracking code to monitor form submissions


Testing

You can test the endpoint using curl:

curl -X POST https://BASE_URL.cloudfunctions.net/webFormSubmit \
-H "Content-Type: application/json" \
-d '{
"teamId": "YOUR_TEAM_ID",
"teamKey": "YOUR_TEAM_KEY",
"formData": {
"contact.firstName": "John",
"contact.lastName": "Doe",
"contact.email": "john.doe@example.com",
"contact.phone": "+1234567890",
"account.name": "Acme Corp",
"deal.description": "Interested in your product",
"sourceName": "Website"
}
}'

Published with Nuclino