Sequel and custom form integration

Learn how to integrate Sequel with a custom form and render on submission

This documentation explains how to integrate Sequel with your custom forms, similar to how Marketo and HubSpot integrations work. When a user submits your form, Sequel will automatically register them for the event and render the event interface.

Overview

Custom form integration with Sequel follows this flow:

  1. User submits your custom form
  2. Your form handler captures the submission
  3. Extract user data (name, email)
  4. Call Sequel registration API
  5. Sequel renders the event interface
  6. User can join the event

This approach gives you complete control over the form design and validation while leveraging Sequel's event management capabilities.

Quick Start

1. Basic HTML Setup

<!DOCTYPE html>
<html>
<head>
    <title>Event Registration</title>
</head>
<body>
    <!-- Your custom form -->
    <form id="customRegistrationForm">
        <input type="text" id="firstName" name="firstName" placeholder="First Name" required>
        <input type="text" id="lastName" name="lastName" placeholder="Last Name" required>
        <input type="email" id="email" name="email" placeholder="Email" required>
        <button type="submit">Register for Event</button>
    </form>

    <!-- Sequel will render here after registration -->
    <div id="sequel_root"></div>

    <!-- Include Sequel script -->
    <script type="module" src="https://prod-assets.sequelvideo.com/uploads/toolkit/sequel.js"></script>
    
    <script>
        document.getElementById('customRegistrationForm').addEventListener('submit', async function(e) {
            e.preventDefault();
            
            // Get form data
            const firstName = document.getElementById('firstName').value;
            const lastName = document.getElementById('lastName').value;
            const email = document.getElementById('email').value;
            
            try {
                // Register user with Sequel
                const registration = await Sequel.registerUserForEvent({
                    name: `${firstName} ${lastName}`,
                    email: email,
                    eventId: 'your-sequel-event-id'
                });
                
                // Hide the form
                document.getElementById('customRegistrationForm').style.display = 'none';
                
                // Render Sequel event interface
                Sequel.renderEvent({
                    eventId: 'your-sequel-event-id',
                    joinCode: registration.joinCode
                });
                
            } catch (error) {
                console.error('Registration failed:', error);
                alert('Registration failed. Please try again.');
            }
        });
    </script>
</body>
</html>

Integration Methods

Method 1: Direct API Integration (Recommended)

This method directly calls the Sequel registration API from your form handler:

// Form submission handler
async function handleFormSubmission(formData) {
    try {
        // Register user with Sequel
        const response = await fetch('https://api.introvoke.com/api/v3/events/your-event-id/registrant', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                name: `${formData.firstName} ${formData.lastName}`,
                email: formData.email,
                resendInvite: true,
                ignoreCustomQuestions: true
            })
        });
        
        if (!response.ok) {
            throw new Error(`Registration failed: ${response.statusText}`);
        }
        
        const registration = await response.json();
        
        // Store join code for future use
        Sequel.setJoinCodeCookie('your-event-id', registration.joinCode);
        
        // Render Sequel event interface
        Sequel.renderEvent({
            eventId: 'your-event-id',
            joinCode: registration.joinCode
        });
        
    } catch (error) {
        console.error('Registration error:', error);
        // Handle error appropriately
    }
}

Method 2: Using Sequel's Built-in Registration Helper

Use Sequel's built-in registration API wrapper:

// Form submission handler using Sequel helper
async function handleFormSubmission(formData) {
    try {
        // Use Sequel's registration helper
        const registration = await Sequel.registerUserForEvent({
            name: `${formData.firstName} ${formData.lastName}`,
            email: formData.email,
            eventId: 'your-sequel-event-id'
        });
        
        // Hide form and show event
        hideForm();
        Sequel.renderEvent({
            eventId: 'your-sequel-event-id',
            joinCode: registration.joinCode
        });
        
    } catch (error) {
        console.error('Registration failed:', error);
        showErrorMessage('Registration failed. Please try again.');
    }
}

Method 3: PostMessage Integration

For iframe-based forms or cross-origin scenarios:

// In your form's iframe or separate domain
function submitForm(formData) {
    // Send registration data to parent window
    window.parent.postMessage({
        type: 'customFormSubmission',
        data: {
            firstName: formData.firstName,
            lastName: formData.lastName,
            email: formData.email,
            eventId: 'your-sequel-event-id'
        }
    }, '*');
}

// In your main page
window.addEventListener('message', async function(event) {
    if (event.data.type === 'customFormSubmission') {
        const { firstName, lastName, email, eventId } = event.data.data;
        
        try {
            const registration = await Sequel.registerUserForEvent({
                name: `${firstName} ${lastName}`,
                email: email,
                eventId: eventId
            });
            
            // Render Sequel event
            Sequel.renderEvent({
                eventId: eventId,
                joinCode: registration.joinCode
            });
            
        } catch (error) {
            console.error('Registration failed:', error);
        }
    }
});

API Reference

Sequel.registerUserForEvent()

Registers a user for a Sequel event and returns registration details.

const registration = await Sequel.registerUserForEvent({
    name: string,        // Full name of the user
    email: string,       // Email address (required)
    eventId: string      // Sequel event ID
});

// Returns:
// {
//   authToken: string,
//   joinCode: string,
//   email: string
// }

Sequel.renderEvent()

Renders the Sequel event interface after registration.

Sequel.renderEvent({
    eventId: string,     // Sequel event ID
    joinCode: string,    // Join code from registration
    hybrid?: boolean,    // Optional: enable hybrid mode
    isPopup?: boolean    // Optional: render in popup mode
});

Sequel.setJoinCodeCookie()

Stores the join code in a cookie for future visits.

Sequel.setJoinCodeCookie(eventId, joinCode);

Direct API Endpoint

For server-side integration or custom implementations:

POST https://api.introvoke.com/api/v3/events/{eventId}/registrant
Content-Type: application/json

{
    "name": "John Doe",
    "email": "[email protected]",
    "resendInvite": true,
    "ignoreCustomQuestions": true
}

Response:

{
    "authToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
    "joinCode": "ABC123DEF456",
    "email": "[email protected]"
}

Complete Examples

Example 1: React Component Integration

import React, { useState } from 'react';

function EventRegistrationForm({ eventId }) {
    const [formData, setFormData] = useState({
        firstName: '',
        lastName: '',
        email: ''
    });
    const [isRegistered, setIsRegistered] = useState(false);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState(null);

    const handleSubmit = async (e) => {
        e.preventDefault();
        setIsLoading(true);
        setError(null);

        try {
            const registration = await window.Sequel.registerUserForEvent({
                name: `${formData.firstName} ${formData.lastName}`,
                email: formData.email,
                eventId: eventId
            });

            setIsRegistered(true);
            
            // Render Sequel event interface
            window.Sequel.renderEvent({
                eventId: eventId,
                joinCode: registration.joinCode
            });

        } catch (err) {
            setError('Registration failed. Please try again.');
            console.error('Registration error:', err);
        } finally {
            setIsLoading(false);
        }
    };

    if (isRegistered) {
        return <div id="sequel_root"></div>;
    }

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <input
                    type="text"
                    placeholder="First Name"
                    value={formData.firstName}
                    onChange={(e) => setFormData({...formData, firstName: e.target.value})}
                    required
                />
            </div>
            <div>
                <input
                    type="text"
                    placeholder="Last Name"
                    value={formData.lastName}
                    onChange={(e) => setFormData({...formData, lastName: e.target.value})}
                    required
                />
            </div>
            <div>
                <input
                    type="email"
                    placeholder="Email"
                    value={formData.email}
                    onChange={(e) => setFormData({...formData, email: e.target.value})}
                    required
                />
            </div>
            {error && <div className="error">{error}</div>}
            <button type="submit" disabled={isLoading}>
                {isLoading ? 'Registering...' : 'Register for Event'}
            </button>
        </form>
    );
}

Example 2: Multi-Step Form with Validation

<!DOCTYPE html>
<html>
<head>
    <title>Multi-Step Event Registration</title>
    <style>
        .form-step { display: none; }
        .form-step.active { display: block; }
        .error { color: red; margin-top: 5px; }
        .success { color: green; }
    </style>
</head>
<body>
    <div id="registration-container">
        <form id="multiStepForm">
            <!-- Step 1: Basic Info -->
            <div class="form-step active" data-step="1">
                <h2>Step 1: Basic Information</h2>
                <input type="text" id="firstName" placeholder="First Name" required>
                <input type="text" id="lastName" placeholder="Last Name" required>
                <button type="button" onclick="nextStep()">Next</button>
            </div>

            <!-- Step 2: Contact Info -->
            <div class="form-step" data-step="2">
                <h2>Step 2: Contact Information</h2>
                <input type="email" id="email" placeholder="Email Address" required>
                <input type="tel" id="phone" placeholder="Phone Number">
                <button type="button" onclick="prevStep()">Previous</button>
                <button type="button" onclick="nextStep()">Next</button>
            </div>

            <!-- Step 3: Company Info -->
            <div class="form-step" data-step="3">
                <h2>Step 3: Company Information</h2>
                <input type="text" id="company" placeholder="Company Name">
                <input type="text" id="jobTitle" placeholder="Job Title">
                <button type="button" onclick="prevStep()">Previous</button>
                <button type="submit">Register for Event</button>
            </div>
        </form>
    </div>

    <div id="sequel_root"></div>

    <script type="module" src="https://prod-assets.sequelvideo.com/uploads/toolkit/sequel.js"></script>
    <script>
        const SEQUEL_EVENT_ID = 'your-sequel-event-id';
        let currentStep = 1;

        function showStep(step) {
            document.querySelectorAll('.form-step').forEach(el => {
                el.classList.remove('active');
            });
            document.querySelector(`[data-step="${step}"]`).classList.add('active');
            currentStep = step;
        }

        function nextStep() {
            if (validateCurrentStep()) {
                showStep(currentStep + 1);
            }
        }

        function prevStep() {
            showStep(currentStep - 1);
        }

        function validateCurrentStep() {
            const currentStepEl = document.querySelector(`[data-step="${currentStep}"]`);
            const inputs = currentStepEl.querySelectorAll('input[required]');
            
            for (let input of inputs) {
                if (!input.value.trim()) {
                    showError(input, 'This field is required');
                    return false;
                }
                if (input.type === 'email' && !isValidEmail(input.value)) {
                    showError(input, 'Please enter a valid email address');
                    return false;
                }
            }
            
            clearErrors();
            return true;
        }

        function isValidEmail(email) {
            return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
        }

        function showError(input, message) {
            clearErrors();
            const error = document.createElement('div');
            error.className = 'error';
            error.textContent = message;
            input.parentNode.appendChild(error);
            input.focus();
        }

        function clearErrors() {
            document.querySelectorAll('.error').forEach(el => el.remove());
        }

        document.getElementById('multiStepForm').addEventListener('submit', async function(e) {
            e.preventDefault();
            
            if (!validateCurrentStep()) return;

            const formData = {
                firstName: document.getElementById('firstName').value,
                lastName: document.getElementById('lastName').value,
                email: document.getElementById('email').value,
                phone: document.getElementById('phone').value,
                company: document.getElementById('company').value,
                jobTitle: document.getElementById('jobTitle').value
            };

            try {
                // Show loading state
                const submitBtn = e.target.querySelector('button[type="submit"]');
                submitBtn.textContent = 'Registering...';
                submitBtn.disabled = true;

                // Register with Sequel
                const registration = await Sequel.registerUserForEvent({
                    name: `${formData.firstName} ${formData.lastName}`,
                    email: formData.email,
                    eventId: SEQUEL_EVENT_ID
                });

                // Optional: Send additional data to your backend
                await fetch('/api/save-registration', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        ...formData,
                        sequelJoinCode: registration.joinCode
                    })
                });

                // Hide form and show Sequel event
                document.getElementById('registration-container').style.display = 'none';
                
                Sequel.renderEvent({
                    eventId: SEQUEL_EVENT_ID,
                    joinCode: registration.joinCode
                });

            } catch (error) {
                console.error('Registration failed:', error);
                showError(document.getElementById('email'), 'Registration failed. Please try again.');
                
                // Reset submit button
                const submitBtn = e.target.querySelector('button[type="submit"]');
                submitBtn.textContent = 'Register for Event';
                submitBtn.disabled = false;
            }
        });
    </script>
</body>
</html>

Example 3: Server-Side Integration (Node.js)

// server.js - Express.js example
const express = require('express');
const axios = require('axios');
const app = express();

app.use(express.json());
app.use(express.static('public'));

// Registration endpoint
app.post('/api/register-for-event', async (req, res) => {
    try {
        const { firstName, lastName, email, eventId } = req.body;
        
        // Validate input
        if (!firstName || !lastName || !email || !eventId) {
            return res.status(400).json({ error: 'Missing required fields' });
        }
        
        // Register with Sequel API
        const sequelResponse = await axios.post(
            `https://api.introvoke.com/api/v3/events/${eventId}/registrant`,
            {
                name: `${firstName} ${lastName}`,
                email: email,
                resendInvite: true,
                ignoreCustomQuestions: true
            },
            {
                headers: {
                    'Content-Type': 'application/json'
                }
            }
        );
        
        const registration = sequelResponse.data;
        
        // Optional: Save to your database
        await saveRegistrationToDatabase({
            firstName,
            lastName,
            email,
            eventId,
            sequelJoinCode: registration.joinCode,
            registeredAt: new Date()
        });
        
        // Return registration details
        res.json({
            success: true,
            joinCode: registration.joinCode,
            authToken: registration.authToken
        });
        
    } catch (error) {
        console.error('Registration error:', error);
        
        if (error.response) {
            // Sequel API error
            res.status(error.response.status).json({
                error: 'Registration failed',
                details: error.response.data
            });
        } else {
            // Server error
            res.status(500).json({
                error: 'Internal server error'
            });
        }
    }
});

async function saveRegistrationToDatabase(data) {
    // Your database logic here
    console.log('Saving registration:', data);
}

app.listen(3000, () => {
    console.log('Server running on port 3000');
});
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Server-Side Registration</title>
</head>
<body>
    <form id="registrationForm">
        <input type="text" id="firstName" placeholder="First Name" required>
        <input type="text" id="lastName" placeholder="Last Name" required>
        <input type="email" id="email" placeholder="Email" required>
        <button type="submit">Register</button>
    </form>

    <div id="sequel_root"></div>

    <script type="module" src="https://prod-assets.sequelvideo.com/uploads/toolkit/sequel.js"></script>
    <script>
        const EVENT_ID = 'your-sequel-event-id';

        document.getElementById('registrationForm').addEventListener('submit', async function(e) {
            e.preventDefault();
            
            const formData = {
                firstName: document.getElementById('firstName').value,
                lastName: document.getElementById('lastName').value,
                email: document.getElementById('email').value,
                eventId: EVENT_ID
            };

            try {
                // Call your server endpoint
                const response = await fetch('/api/register-for-event', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(formData)
                });

                if (!response.ok) {
                    throw new Error('Registration failed');
                }

                const result = await response.json();
                
                // Hide form
                document.getElementById('registrationForm').style.display = 'none';
                
                // Render Sequel event
                Sequel.renderEvent({
                    eventId: EVENT_ID,
                    joinCode: result.joinCode
                });

            } catch (error) {
                console.error('Registration error:', error);
                alert('Registration failed. Please try again.');
            }
        });
    </script>
</body>
</html>

Event Handling

Listening for Registration Events

You can listen for registration success/failure events:

// Listen for successful registration
window.addEventListener('sequel:registration:success', function(event) {
    const { eventId, joinCode, email } = event.detail;
    console.log('User registered successfully:', { eventId, joinCode, email });
    
    // Optional: Track in analytics
    gtag('event', 'registration', {
        event_category: 'sequel',
        event_label: eventId
    });
});

// Listen for registration errors
window.addEventListener('sequel:registration:error', function(event) {
    const { error, eventId } = event.detail;
    console.error('Registration failed:', error);
    
    // Optional: Show user-friendly error message
    showErrorNotification('Registration failed. Please try again.');
});

Custom Event Dispatching

If you want to dispatch custom events from your form:

function dispatchRegistrationEvent(type, data) {
    const event = new CustomEvent(`sequel:registration:${type}`, {
        detail: data,
        bubbles: true
    });
    window.dispatchEvent(event);
}

// Usage in your form handler
try {
    const registration = await Sequel.registerUserForEvent(userData);
    dispatchRegistrationEvent('success', {
        eventId: userData.eventId,
        joinCode: registration.joinCode,
        email: userData.email
    });
} catch (error) {
    dispatchRegistrationEvent('error', {
        error: error.message,
        eventId: userData.eventId
    });
}

Best Practices

1. Form Validation

Always validate form data before submitting:

function validateRegistrationForm(formData) {
    const errors = [];
    
    if (!formData.firstName?.trim()) {
        errors.push('First name is required');
    }
    
    if (!formData.lastName?.trim()) {
        errors.push('Last name is required');
    }
    
    if (!formData.email?.trim()) {
        errors.push('Email is required');
    } else if (!isValidEmail(formData.email)) {
        errors.push('Please enter a valid email address');
    }
    
    return errors;
}

function isValidEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

2. Error Handling

Implement comprehensive error handling:

async function registerUser(formData) {
    try {
        const registration = await Sequel.registerUserForEvent(formData);
        return { success: true, data: registration };
    } catch (error) {
        console.error('Registration error:', error);
        
        // Handle specific error types
        if (error.message.includes('already registered')) {
            return { 
                success: false, 
                error: 'You are already registered for this event.' 
            };
        } else if (error.message.includes('event not found')) {
            return { 
                success: false, 
                error: 'Event not found. Please check the event ID.' 
            };
        } else {
            return { 
                success: false, 
                error: 'Registration failed. Please try again.' 
            };
        }
    }
}

3. Loading States

Provide visual feedback during registration:

function setLoadingState(isLoading) {
    const submitBtn = document.querySelector('button[type="submit"]');
    const form = document.getElementById('registrationForm');
    
    if (isLoading) {
        submitBtn.textContent = 'Registering...';
        submitBtn.disabled = true;
        form.style.opacity = '0.6';
    } else {
        submitBtn.textContent = 'Register for Event';
        submitBtn.disabled = false;
        form.style.opacity = '1';
    }
}

4. Progressive Enhancement

Ensure your form works without JavaScript:

<form action="/fallback-registration" method="POST" id="registrationForm">
    <input type="text" name="firstName" required>
    <input type="text" name="lastName" required>
    <input type="email" name="email" required>
    <input type="hidden" name="eventId" value="your-event-id">
    <button type="submit">Register for Event</button>
</form>

<script>
    // Enhance with JavaScript if available
    if (window.Sequel) {
        document.getElementById('registrationForm').addEventListener('submit', handleJSSubmission);
    }
</script>

5. Cookie Management

Properly handle join codes for returning users:

async function initializeEventPage(eventId) {
    // Check if user already has a join code
    const existingJoinCode = Sequel.getJoinCodeFromCookie(eventId);
    
    if (existingJoinCode) {
        // User is already registered, show event directly
        document.getElementById('registrationForm').style.display = 'none';
        Sequel.renderEvent({
            eventId: eventId,
            joinCode: existingJoinCode
        });
    } else {
        // Show registration form
        document.getElementById('registrationForm').style.display = 'block';
    }
}

Troubleshooting

Common Issues

1. "Event not found" Error

// Check your event ID
const eventId = 'your-sequel-event-id'; // Must be valid Sequel event ID

// Verify event exists
const event = await Sequel.getEvent(eventId);
if (!event) {
    console.error('Event not found:', eventId);
}

2. CORS Issues

If you're calling the API directly from the browser:

// Use Sequel's built-in methods instead of direct fetch
// ❌ This might cause CORS issues:
fetch('https://api.introvoke.com/api/v3/events/...')

// ✅ Use this instead:
Sequel.registerUserForEvent({...})

3. "Element not found" Error

// Ensure sequel_root exists before rendering
const sequelRoot = document.getElementById('sequel_root');
if (!sequelRoot) {
    console.error('sequel_root element not found');
    return;
}

4. Registration API Failures

// Add retry logic for network issues
async function registerWithRetry(userData, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            return await Sequel.registerUserForEvent(userData);
        } catch (error) {
            if (i === maxRetries - 1) throw error;
            await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
        }
    }
}

Debug Mode

Enable debug logging:

// Enable Sequel debug mode
window.SEQUEL_DEBUG = true;

// Your registration code here...

Network Debugging

Check network requests in browser dev tools:

  1. Open Developer Tools (F12)
  2. Go to Network tab
  3. Submit your form
  4. Look for requests to api.introvoke.com
  5. Check response status and error messages

Support

For additional help with custom form integration: