Forms API
Overview
The Forms API provides comprehensive management of web forms for lead capture, data collection, and customer engagement. Built on the powerful SurveyJS framework, Stack9 Forms enable you to create sophisticated multi-page forms with advanced logic, custom themes, and seamless integration with your marketing automation workflows.
Resource Description
Forms in Stack9 consist of:
- Form Configuration: Core form settings including name, type, and CORS configuration
- SurveyJS Schema: Complete form structure with pages, questions, validation rules, and conditional logic
- Form Themes: Visual styling and branding options for consistent user experience
- Submission Management: Capture and process form responses with automatic subscriber creation
- Post-Submit Actions: Configurable actions triggered after form submission
- Analytics & Tracking: Monitor form performance, conversion rates, and user behavior
Key Features
- Visual Form Builder: Create forms using SurveyJS's powerful drag-and-drop interface
- Advanced Question Types: Support for 20+ question types including matrices, ratings, and file uploads
- Conditional Logic: Show/hide questions based on previous answers
- Multi-Page Forms: Create wizard-style forms with progress indicators
- Custom Themes: Apply branded themes with full control over colors, fonts, and layout
- CORS Configuration: Secure form embedding with domain whitelist controls
- Real-Time Validation: Client-side and server-side validation rules
- Submission Tracking: Complete audit trail of all form submissions
- Lead Capture Integration: Automatic subscriber creation from form submissions
- Journey Triggers: Initiate marketing automation workflows on form completion
Authentication
All endpoints require API key authentication:
X-API-Key: your-api-key-here
Form Settings
Get Form Settings
Retrieve configuration options and metadata for form management.
GET /api/forms/settings
Example Request
curl -X GET \
https://apis.app.stack9.co/api/forms/settings \
-H 'X-API-Key: your-api-key-here'
Example Response
{
"formTypes": [
{
"key": "embedded_form",
"name": "Embedded Form",
"description": "Form that can be embedded in external websites"
}
],
"postSubmitActionTypes": [
{
"key": "redirect",
"name": "Redirect to URL",
"description": "Redirect user to a specific URL after submission"
},
{
"key": "show_message",
"name": "Show Message",
"description": "Display a custom thank you message"
},
{
"key": "trigger_journey",
"name": "Trigger Journey",
"description": "Start a marketing automation journey"
}
],
"surveyLicenseKey": "xxxxx-xxxxx-xxxxx"
}
Form Management
Create Form
Create a new form with SurveyJS configuration and settings.
POST /api/forms
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name for the form |
allowed_origins | array | Yes | Domains allowed to embed this form. Use ["*"] for all domains |
active | boolean | Yes | Whether form accepts submissions |
form_type | string | Yes | Type of form (must be "embedded_form") |
business_unit | string | No | Business unit identifier |
surveyJsOptions | object | Yes | Complete SurveyJS form configuration |
surveyJsOptions.title | string | No | Form title displayed to users |
surveyJsOptions.description | string | No | Form description or instructions |
surveyJsOptions.logo | string | No | Logo URL for form header |
surveyJsOptions.pages | array | Yes | Array of form pages with questions |
surveyJsOptions.completedHtml | string | No | HTML shown after form completion |
surveyJsOptions.showProgressBar | string | No | Progress bar display ("top", "bottom", "both", "none") |
Example Request
curl -X POST \
https://apis.app.stack9.co/api/forms \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"name": "Newsletter Signup",
"allowed_origins": ["https://example.com", "https://www.example.com"],
"active": true,
"form_type": "embedded_form",
"business_unit": "marketing",
"surveyJsOptions": {
"title": "Join Our Newsletter",
"description": "Get weekly updates and exclusive content",
"logo": "https://example.com/logo.png",
"logoWidth": "200px",
"logoHeight": "60px",
"logoPosition": "right",
"showProgressBar": "top",
"completedHtml": "<h3>Thank you!</h3><p>You have been successfully subscribed.</p>",
"pages": [
{
"name": "page1",
"title": "Contact Information",
"elements": [
{
"type": "text",
"name": "email",
"title": "Email Address",
"inputType": "email",
"isRequired": true,
"validators": [
{
"type": "email",
"text": "Please enter a valid email address"
}
]
},
{
"type": "text",
"name": "firstName",
"title": "First Name",
"isRequired": true
},
{
"type": "text",
"name": "lastName",
"title": "Last Name",
"isRequired": false
},
{
"type": "dropdown",
"name": "interests",
"title": "What are you interested in?",
"choices": [
"Product Updates",
"Industry News",
"Tips & Tutorials",
"Case Studies"
],
"isRequired": true
}
]
},
{
"name": "page2",
"title": "Preferences",
"elements": [
{
"type": "radiogroup",
"name": "frequency",
"title": "How often would you like to hear from us?",
"choices": [
"Weekly",
"Bi-weekly",
"Monthly"
],
"defaultValue": "Weekly"
},
{
"type": "checkbox",
"name": "consent",
"title": "I agree to receive marketing communications",
"isRequired": true,
"validators": [
{
"type": "expression",
"text": "You must agree to continue",
"expression": "{consent} = true"
}
]
}
]
}
]
}
}'
Example Response
{
"code": "form_Xk9mNp2Q"
}
List Forms
Retrieve a paginated list of forms with advanced filtering options.
POST /api/forms/list
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | number | Yes | Page number (1-based) |
limit | number | Yes | Items per page (max: 100) |
search | string | No | Search across form names |
filters | object | Yes | Filter criteria object |
filters.name | object | No | Filter by form name |
filters.active | object | No | Filter by active status |
filters.form_type | object | No | Filter by form type |
filters.created_at | object | No | Filter by creation date |
Filter Operations
Each filter field supports these operations:
eq: Equalsne: Not equalslike: Contains (for strings)gt,gte,lt,lte: Comparison operatorsin: Value in arraybetween: Between two values
Example Request
curl -X POST \
https://apis.app.stack9.co/api/forms/list \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"page": 1,
"limit": 20,
"search": "newsletter",
"filters": {
"active": {
"operation": "eq",
"value": true
},
"form_type": {
"operation": "eq",
"value": "embedded_form"
},
"created_at": {
"operation": "gte",
"value": "2024-01-01T00:00:00.000Z"
}
}
}'
Example Response
{
"results": [
{
"version": 2,
"created_at": "2024-01-15T10:30:00.123Z",
"updated_at": "2024-03-20T14:45:30.456Z",
"code": "form_Xk9mNp2Q",
"public_code": "pub_form_Abc123",
"name": "Newsletter Signup",
"allowed_origins": ["https://example.com"],
"active": true,
"form_type": "embedded_form",
"business_unit": "marketing",
"surveyJsOptions": {
"title": "Join Our Newsletter",
"pages": [...]
},
"submission_count": 1523,
"conversion_rate": 68.5
}
],
"total": 15,
"totalPages": 1
}
Get Form by Code
Retrieve a specific form by its unique code.
GET /api/forms/{code}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Form code (path parameter) |
Example Request
curl -X GET \
https://apis.app.stack9.co/api/forms/form_Xk9mNp2Q \
-H 'X-API-Key: your-api-key-here'
Example Response
{
"version": 2,
"created_at": "2024-01-15T10:30:00.123Z",
"updated_at": "2024-03-20T14:45:30.456Z",
"code": "form_Xk9mNp2Q",
"public_code": "pub_form_Abc123",
"name": "Newsletter Signup",
"allowed_origins": ["https://example.com", "https://www.example.com"],
"active": true,
"form_type": "embedded_form",
"business_unit": "marketing",
"surveyJsOptions": {
"title": "Join Our Newsletter",
"description": "Get weekly updates and exclusive content",
"logo": "https://example.com/logo.png",
"logoWidth": "200px",
"logoHeight": "60px",
"pages": [
{
"name": "page1",
"title": "Contact Information",
"elements": [
{
"type": "text",
"name": "email",
"title": "Email Address",
"inputType": "email",
"isRequired": true
}
]
}
],
"completedHtml": "<h3>Thank you!</h3>"
}
}
Get Form Schema with Theme
Retrieve form configuration along with its associated theme for rendering.
GET /api/forms/{code}/schema
This endpoint returns the complete form definition including theme configuration, useful for embedding and rendering forms in external applications.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Form code (path parameter) |
Example Request
curl -X GET \
https://apis.app.stack9.co/api/forms/form_Xk9mNp2Q/schema \
-H 'X-API-Key: your-api-key-here'
Example Response
{
"form": {
"code": "form_Xk9mNp2Q",
"public_code": "pub_form_Abc123",
"name": "Newsletter Signup",
"surveyJsOptions": {
"title": "Join Our Newsletter",
"pages": [...]
}
},
"theme": {
"code": "theme_Def456",
"name": "Brand Theme",
"surveyJsThemeOptions": {
"themeName": "modern",
"colorPalette": "light",
"isPanelless": true,
"primaryColor": "#2563eb",
"backgroundImage": "https://example.com/bg.jpg",
"backgroundImageFit": "cover"
}
}
}
Update Form
Update an existing form's configuration.
PUT /api/forms/{code}
Parameters
Same as Create Form. All fields provided will update the existing values.
Example Request
curl -X PUT \
https://apis.app.stack9.co/api/forms/form_Xk9mNp2Q \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"name": "Updated Newsletter Form",
"allowed_origins": ["*"],
"active": true,
"form_type": "embedded_form",
"surveyJsOptions": {
"title": "Subscribe to Our Newsletter",
"description": "Updated description",
"pages": [...]
}
}'
Example Response
{
"success": true,
"code": "form_Xk9mNp2Q"
}
Form Themes
Create Form Theme
Create a custom theme for styling forms.
POST /api/form_themes
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Theme name |
surveyJsThemeOptions | object | Yes | SurveyJS theme configuration |
surveyJsThemeOptions.themeName | string | No | Base theme name |
surveyJsThemeOptions.colorPalette | string | No | Color palette ("light" or "dark") |
surveyJsThemeOptions.isPanelless | boolean | No | Remove panel borders |
surveyJsThemeOptions.backgroundImage | string | No | Background image URL |
surveyJsThemeOptions.backgroundImageFit | string | No | Image fit mode ("auto", "contain", "cover") |
surveyJsThemeOptions.backgroundOpacity | number | No | Background opacity (0-1) |
surveyJsThemeOptions.primaryColor | string | No | Primary brand color |
surveyJsThemeOptions.header | object | No | Header configuration |
Example Request
curl -X POST \
https://apis.app.stack9.co/api/form_themes \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"name": "Modern Blue Theme",
"surveyJsThemeOptions": {
"themeName": "modern",
"colorPalette": "light",
"isPanelless": true,
"primaryColor": "#2563eb",
"backgroundImage": "https://example.com/gradient-bg.jpg",
"backgroundImageFit": "cover",
"backgroundImageAttachment": "fixed",
"backgroundOpacity": 0.1,
"header": {
"height": 200,
"inheritWidthFrom": "container",
"textAreaWidth": 768,
"logoPositionX": "right",
"logoPositionY": "middle",
"titlePositionX": "left",
"titlePositionY": "middle",
"descriptionPositionX": "left",
"descriptionPositionY": "bottom",
"overlapEnabled": false,
"backgroundImageOpacity": 0.8
},
"cssVariables": {
"--primary-color": "#2563eb",
"--primary-light": "#60a5fa",
"--primary-dark": "#1e40af",
"--background": "#ffffff",
"--background-dim": "#f9fafb",
"--foreground": "#1f2937",
"--border": "#e5e7eb",
"--font-family": "Inter, system-ui, sans-serif",
"--base-unit": "8px",
"--corner-radius": "6px"
}
}
}'
Example Response
{
"code": "theme_Def456"
}
List Form Themes
Retrieve available form themes.
POST /api/form_themes/list
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | number | Yes | Page number (1-based) |
limit | number | Yes | Items per page (max: 100) |
filters | object | No | Filter criteria |
Example Request
curl -X POST \
https://apis.app.stack9.co/api/form_themes/list \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"page": 1,
"limit": 20,
"filters": {}
}'
Example Response
{
"results": [
{
"version": 1,
"created_at": "2024-01-15T10:30:00.123Z",
"updated_at": "2024-01-15T10:30:00.123Z",
"code": "theme_Def456",
"name": "Modern Blue Theme",
"surveyJsThemeOptions": {
"themeName": "modern",
"colorPalette": "light",
"primaryColor": "#2563eb"
}
},
{
"code": "theme_Default",
"name": "Default Theme",
"surveyJsThemeOptions": {
"themeName": "default"
}
}
],
"total": 5,
"totalPages": 1
}
Get Form Theme by Code
Retrieve a specific theme configuration.
GET /api/form_themes/{code}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Theme code (path parameter) |
Example Request
curl -X GET \
https://apis.app.stack9.co/api/form_themes/theme_Def456 \
-H 'X-API-Key: your-api-key-here'
Example Response
{
"version": 1,
"created_at": "2024-01-15T10:30:00.123Z",
"updated_at": "2024-01-15T10:30:00.123Z",
"code": "theme_Def456",
"name": "Modern Blue Theme",
"surveyJsThemeOptions": {
"themeName": "modern",
"colorPalette": "light",
"isPanelless": true,
"primaryColor": "#2563eb",
"backgroundImage": "https://example.com/gradient-bg.jpg",
"backgroundImageFit": "cover",
"header": {
"height": 200,
"logoPositionX": "right",
"titlePositionX": "left"
}
}
}
Update Form Theme
Update an existing theme configuration.
PUT /api/form_themes/{code}
Parameters
Same as Create Form Theme.
Example Request
curl -X PUT \
https://apis.app.stack9.co/api/form_themes/theme_Def456 \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"name": "Updated Blue Theme",
"surveyJsThemeOptions": {
"themeName": "modern",
"colorPalette": "dark",
"primaryColor": "#3b82f6"
}
}'
Form Submissions
Create Form Submission
Submit data to a form. This endpoint is typically called from embedded forms.
POST /api/form_submissions
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
form_code | string | Yes | The form code this submission is for |
email_address | string | Yes | Responder's email address |
form_data | object | Yes | Form response data as key-value pairs |
Example Request
curl -X POST \
https://apis.app.stack9.co/api/form_submissions \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"form_code": "form_Xk9mNp2Q",
"email_address": "john.doe@example.com",
"form_data": {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"interests": "Product Updates",
"frequency": "Weekly",
"consent": true,
"company": "Acme Corp",
"phone": "+1-555-0123"
}
}'
Example Response
{
"submission_id": "sub_9kL3mP5n"
}
Error Responses
400 Bad Request
{
"message": "Invalid submission data",
"code": "INVALID_SUBMISSION",
"issues": [
{
"field": "email_address",
"message": "Invalid email format"
}
]
}
404 Not Found
{
"message": "Form not found or inactive",
"code": "FORM_NOT_FOUND"
}
List Form Submissions
Retrieve form submissions with filtering options.
POST /api/form_submissions/list
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | number | Yes | Page number (1-based) |
limit | number | Yes | Items per page (max: 100) |
filters | object | Yes | Filter criteria |
filters.form_code | object | No | Filter by form |
filters.email_address | object | No | Filter by email |
filters.created_at | object | No | Filter by submission date |
Example Request
curl -X POST \
https://apis.app.stack9.co/api/form_submissions/list \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"page": 1,
"limit": 50,
"filters": {
"form_code": {
"operation": "eq",
"value": "form_Xk9mNp2Q"
},
"created_at": {
"operation": "between",
"value": ["2024-01-01T00:00:00.000Z", "2024-12-31T23:59:59.999Z"]
}
}
}'
Example Response
{
"results": [
{
"id": "sub_9kL3mP5n",
"created_at": "2024-03-20T14:30:15.234Z",
"form_code": "form_Xk9mNp2Q",
"form_name": "Newsletter Signup",
"email_address": "john.doe@example.com",
"form_data": {
"firstName": "John",
"lastName": "Doe",
"interests": "Product Updates",
"frequency": "Weekly"
},
"subscriber_created": true,
"journey_triggered": false,
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0..."
}
],
"total": 1523,
"totalPages": 31
}
Form Embedding
Embedding Forms in Websites
Forms can be embedded in external websites using the following methods:
Method 1: iFrame Embedding
<iframe
src="https://forms.stack9.io/embed/pub_form_Abc123"
width="100%"
height="600"
frameborder="0"
style="border: 0; max-width: 100%;">
</iframe>
Method 2: JavaScript SDK
<!-- Stack9 Forms SDK -->
<script src="https://cdn.stack9.io/forms/v1/sdk.js"></script>
<div id="stack9-form"></div>
<script>
Stack9Forms.render({
container: 'stack9-form',
formCode: 'pub_form_Abc123',
theme: 'theme_Def456',
onComplete: function(submission) {
console.log('Form submitted:', submission.id);
// Custom completion logic
},
onError: function(error) {
console.error('Form error:', error);
}
});
</script>
Method 3: React Component
import { Stack9Form } from '@stack9/forms-react';
function NewsletterSignup() {
const handleComplete = (submission) => {
console.log('Form submitted:', submission.id);
};
return (
<Stack9Form
formCode="pub_form_Abc123"
theme="theme_Def456"
onComplete={handleComplete}
customData={{
source: 'website',
campaign: 'spring-2024'
}}
/>
);
}
CORS Configuration
To embed forms on external domains, configure the allowed_origins field:
{
"allowed_origins": [
"https://example.com",
"https://www.example.com",
"https://blog.example.com"
]
}
Use ["*"] to allow embedding on any domain (not recommended for production).
Advanced Features
Conditional Logic
Implement dynamic form behavior based on user responses:
{
"pages": [
{
"elements": [
{
"type": "radiogroup",
"name": "customerType",
"title": "Are you a new or existing customer?",
"choices": ["New Customer", "Existing Customer"]
},
{
"type": "text",
"name": "accountNumber",
"title": "Account Number",
"visibleIf": "{customerType} = 'Existing Customer'",
"isRequired": true
},
{
"type": "dropdown",
"name": "howHeard",
"title": "How did you hear about us?",
"visibleIf": "{customerType} = 'New Customer'",
"choices": ["Google", "Social Media", "Friend", "Other"]
}
]
}
]
}
Custom Validation
Add custom validation rules to form fields:
{
"elements": [
{
"type": "text",
"name": "age",
"title": "Age",
"inputType": "number",
"validators": [
{
"type": "numeric",
"minValue": 18,
"maxValue": 120,
"text": "Age must be between 18 and 120"
}
]
},
{
"type": "text",
"name": "couponCode",
"title": "Coupon Code",
"validators": [
{
"type": "regex",
"regex": "^[A-Z]{3}-[0-9]{4}$",
"text": "Invalid coupon format (e.g., ABC-1234)"
}
]
}
]
}
Multi-Language Support
Configure forms for multiple languages:
{
"locale": "es",
"title": {
"default": "Newsletter Signup",
"es": "Suscripción al Boletín",
"fr": "Inscription à la Newsletter"
},
"pages": [
{
"elements": [
{
"type": "text",
"name": "email",
"title": {
"default": "Email Address",
"es": "Dirección de Correo",
"fr": "Adresse E-mail"
}
}
]
}
]
}
File Upload Support
Enable file uploads in forms:
{
"elements": [
{
"type": "file",
"name": "resume",
"title": "Upload Resume",
"acceptedTypes": ".pdf,.doc,.docx",
"maxSize": 5242880,
"storeDataAsText": false,
"waitForUpload": true
}
]
}
Integration with Marketing Automation
Automatic Subscriber Creation
Form submissions automatically create or update subscriber profiles:
- New Subscribers: Created with form data mapped to profile fields
- Existing Subscribers: Profile updated with latest form data
- Custom Field Mapping: Map form fields to subscriber custom fields
- Audience Assignment: Auto-assign to specified audiences
- Subscription Preferences: Set initial subscription preferences
Journey Triggers
Configure forms to trigger marketing journeys on submission:
{
"post_submit_actions": [
{
"type": "trigger_journey",
"journey_id": "journey_Welcome123",
"data_mapping": {
"firstName": "form.firstName",
"product_interest": "form.interests"
}
}
]
}
Lead Scoring
Automatically score leads based on form responses:
{
"scoring_rules": [
{
"field": "company_size",
"value": "Enterprise",
"score": 50
},
{
"field": "budget",
"value": "> $100,000",
"score": 30
},
{
"field": "timeline",
"value": "This Quarter",
"score": 20
}
]
}
Analytics and Reporting
Form Performance Metrics
Track key performance indicators:
- Views: Number of times form was loaded
- Starts: Users who began filling the form
- Completions: Successful submissions
- Conversion Rate: Completions / Views
- Abandonment Rate: (Starts - Completions) / Starts
- Average Time: Mean time to complete
- Drop-off Points: Questions where users abandon
Submission Analytics
Analyze submission patterns:
GET /api/forms/{code}/analytics?period=30d
{
"summary": {
"total_views": 5234,
"total_starts": 2156,
"total_completions": 1523,
"conversion_rate": 29.1,
"abandonment_rate": 29.4
},
"daily_metrics": [
{
"date": "2024-03-20",
"views": 187,
"starts": 76,
"completions": 52,
"conversion_rate": 27.8
}
],
"field_completion": {
"email": 100,
"firstName": 98.5,
"lastName": 76.3,
"phone": 45.2
}
}
Best Practices
Form Design
- Keep Forms Short: Limit to essential fields only
- Progressive Disclosure: Use multi-page forms for complex data collection
- Clear Labels: Use descriptive field labels and help text
- Mobile Optimization: Test forms on mobile devices
- Smart Defaults: Pre-fill known information when possible
Conversion Optimization
- Reduce Friction: Minimize required fields
- Social Proof: Add testimonials or trust badges
- Progress Indicators: Show completion progress on multi-page forms
- Inline Validation: Provide immediate feedback on errors
- Clear CTAs: Use action-oriented submit button text
Security Considerations
- CORS Configuration: Restrict to known domains
- Rate Limiting: Implement submission rate limits
- CAPTCHA: Add CAPTCHA for public forms
- Data Validation: Validate all inputs server-side
- SSL/TLS: Always use HTTPS for form submission
GDPR Compliance
- Consent Checkboxes: Include explicit consent fields
- Privacy Policy: Link to privacy policy
- Data Minimization: Collect only necessary data
- Right to Deletion: Provide mechanism to delete submissions
- Audit Trail: Maintain logs of consent and data processing
Error Handling
Common Error Codes
| Code | Description | Resolution |
|---|---|---|
FORM_NOT_FOUND | Form code doesn't exist | Verify form code |
FORM_INACTIVE | Form is not accepting submissions | Activate form in settings |
CORS_ERROR | Domain not in allowed origins | Add domain to allowed_origins |
VALIDATION_ERROR | Form data validation failed | Check field requirements |
RATE_LIMIT | Too many submissions | Implement client-side throttling |
DUPLICATE_SUBMISSION | Duplicate submission detected | Prevent double-submits |
Retry Strategy
Implement exponential backoff for transient errors:
async function submitFormWithRetry(data, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await submitForm(data);
} catch (error) {
lastError = error;
if (error.code === 'RATE_LIMIT' || error.code === 'SERVER_ERROR') {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error; // Non-retryable error
}
}
}
throw lastError;
}
Code Examples
Creating a Multi-Step Lead Generation Form
const leadGenForm = {
name: "Enterprise Lead Generation",
active: true,
form_type: "embedded_form",
allowed_origins: ["https://enterprise.example.com"],
surveyJsOptions: {
title: "Get a Custom Quote",
showProgressBar: "top",
pages: [
{
name: "contact",
title: "Contact Information",
elements: [
{
type: "text",
name: "firstName",
title: "First Name",
isRequired: true
},
{
type: "text",
name: "lastName",
title: "Last Name",
isRequired: true
},
{
type: "text",
name: "email",
title: "Business Email",
inputType: "email",
isRequired: true,
validators: [{
type: "email",
text: "Please enter a valid business email"
}]
},
{
type: "text",
name: "phone",
title: "Phone Number",
inputType: "tel",
isRequired: true
}
]
},
{
name: "company",
title: "Company Information",
elements: [
{
type: "text",
name: "company",
title: "Company Name",
isRequired: true
},
{
type: "dropdown",
name: "industry",
title: "Industry",
isRequired: true,
choices: [
"Technology",
"Healthcare",
"Finance",
"Retail",
"Manufacturing",
"Other"
]
},
{
type: "radiogroup",
name: "companySize",
title: "Company Size",
isRequired: true,
choices: [
"1-10 employees",
"11-50 employees",
"51-200 employees",
"201-1000 employees",
"1000+ employees"
]
}
]
},
{
name: "requirements",
title: "Your Requirements",
elements: [
{
type: "checkbox",
name: "products",
title: "Which products are you interested in?",
isRequired: true,
choices: [
"Email Marketing",
"SMS Marketing",
"Marketing Automation",
"Customer Data Platform",
"Analytics & Reporting"
],
minSelectedChoices: 1
},
{
type: "radiogroup",
name: "budget",
title: "Estimated Annual Budget",
isRequired: true,
choices: [
"< $10,000",
"$10,000 - $50,000",
"$50,000 - $100,000",
"$100,000 - $500,000",
"> $500,000"
]
},
{
type: "comment",
name: "message",
title: "Additional Requirements",
rows: 4
}
]
}
],
completedHtml: "<h3>Thank you for your interest!</h3><p>Our team will contact you within 24 hours.</p>"
}
};
// Create the form
const response = await fetch('https://apis.app.stack9.co/api/forms', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify(leadGenForm)
});
const { code } = await response.json();
console.log(`Form created: ${code}`);
Handling Form Submissions with Webhooks
// Express.js webhook handler
app.post('/webhooks/form-submission', async (req, res) => {
const { form_code, email_address, form_data } = req.body;
try {
// Score the lead
const leadScore = calculateLeadScore(form_data);
// Create or update subscriber
const subscriber = await createOrUpdateSubscriber({
email: email_address,
firstName: form_data.firstName,
lastName: form_data.lastName,
customFields: {
company: form_data.company,
industry: form_data.industry,
leadScore: leadScore
},
audiences: ['leads', form_data.industry.toLowerCase()]
});
// Trigger appropriate journey based on score
if (leadScore >= 80) {
await triggerJourney('hot-lead-nurture', subscriber.id);
await notifySalesTeam(subscriber, form_data);
} else if (leadScore >= 50) {
await triggerJourney('warm-lead-nurture', subscriber.id);
} else {
await triggerJourney('cold-lead-nurture', subscriber.id);
}
// Send to CRM
await syncToCRM({
email: email_address,
data: form_data,
score: leadScore
});
res.json({ success: true });
} catch (error) {
console.error('Submission processing failed:', error);
res.status(500).json({ error: 'Processing failed' });
}
});
function calculateLeadScore(data) {
let score = 0;
// Company size scoring
if (data.companySize === '1000+ employees') score += 30;
else if (data.companySize === '201-1000 employees') score += 20;
else if (data.companySize === '51-200 employees') score += 10;
// Budget scoring
if (data.budget === '> $500,000') score += 40;
else if (data.budget === '$100,000 - $500,000') score += 30;
else if (data.budget === '$50,000 - $100,000') score += 20;
// Product interest scoring
const productCount = data.products?.length || 0;
score += productCount * 5;
// Industry scoring
if (['Technology', 'Finance'].includes(data.industry)) score += 10;
return Math.min(score, 100);
}
Troubleshooting
Form Not Loading
Issue: Form doesn't appear when embedded
Solutions:
- Check CORS configuration in
allowed_origins - Verify form is active
- Check browser console for JavaScript errors
- Ensure form code is correct
- Test with
allowed_origins: ["*"]temporarily
Submissions Not Recording
Issue: Form submits but no data saved
Solutions:
- Verify API key has write permissions
- Check form_code matches active form
- Validate email_address format
- Check rate limits
- Review server logs for errors
Styling Issues
Issue: Form doesn't match website design
Solutions:
- Create custom theme with matching colors
- Use CSS overrides in embedding page
- Set isPanelless: true for minimal styling
- Adjust container width and responsive settings
- Test across different browsers
Performance Optimization
Issue: Form loads slowly
Solutions:
- Minimize number of form fields
- Lazy load form on user interaction
- Use CDN for form assets
- Enable browser caching
- Optimize image sizes in themes