Quick Start Guide

Get Started with FormSubmits

Follow this guide to set up your first form and start collecting submissions in minutes.

5 minutes setup

No backend required

Free plan available

Step 1

Create a Form

After creating your account, the first step is to create a form in the FormSubmits web-app. This defines submission rules and what fields your form will accept and how they should be validated.

How to create a form:

  • Go to Forms page in your dashboard

  • Click "Create Form" button

  • Give your form a name (e.g., "Contact Form", "Newsletter Signup") and configure other form settings

  • Add fields that match your HTML form inputs (name, email, message, etc.)

  • Configure validation rules for each field (required, min/max length, format)

  • Save the form and copy the Form ID — you'll need it for your HTML form

Pro Tip: Field Names Must Match

The field names you configure in the dashboard must match the name attributes in your HTML form. For example, if you add a field named "email" in the dashboard, your HTML input should be <input name="email" />.

Step 2
Optional

Add and Verify Email Address

If you want to receive email notifications when someone submits your form, you need to add and verify your email address first. This step helps prevent abuse of our email service.

How to add an email address:

  • Click "Add Email Address"

  • Enter a display name (e.g., "Sales Team") and the email address

  • Click "Confirm" button next to the newly added email

  • Agree to send a verification email with a confirmation code

  • Check your inbox, copy the code, and enter it in the dashboard

  • Once verified, you can use this email in your form's notification settings

Note: This step is optional. You can skip it if you don't need email notifications and prefer to check submissions directly in the dashboard or send them to external services via webhooks/connectors.

Step 3
Paid Plans

Set Up Connectors

Connectors allow you to send form submissions to external services automatically. Use your own email provider, store files in your cloud storage, or integrate with productivity tools.

Free Plan Limitation

Connectors are not available on the Free plan. Upgrade to a paid plan to unlock integrations with external services. On the Free plan, you can still view submissions in the dashboard and receive basic email notifications.

View Plans

Email Services (BYOE)

SendGrid, AWS SES, or your own SMTP server for branded email notifications

Cloud Storage (BYOS)

Store submissions and/or uploaded files in your AWS S3, Azure Blob, or Google Cloud Storage

Communication

Send submissions to Slack, Discord, or Telegram channels

Productivity

Append data to Google Sheets, Notion, Airtable, or HubSpot

What are BYOE and BYOS?

BYOE (Bring Your Own Email) lets you use your own email service (like SendGrid or AWS SES) to send notifications from your domain. BYOS (Bring Your Own Storage) lets you store uploaded files (and even form data as JSON file with metadata) directly in your cloud storage instead of ours. Both features give you full control over your data and branding.

You can skip this step for now and set up connectors later. Your forms will work without them — submissions will be stored in FormSubmits and you can view them in the dashboard.

Step 4

Create Your HTML Form

Now it's time to create the HTML form on your website. Point your form's action attribute to the FormSubmits API endpoint with your Form ID.

Key Points:

  • Form action URL: https://api.formsubmits.com/api/submissions/YOUR_FORM_ID
  • Use method="POST" for all forms
  • Input name attributes must match field names in dashboard
  • By default, after submission user is redirected back with ?submission=success or ?submission=failed

Simple form that submits directly and redirects back to the page

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Contact Us</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 500px; margin: 2rem auto; padding: 0 1rem; }
    .form-group { margin-bottom: 1rem; }
    label { display: block; margin-bottom: 0.25rem; font-weight: 500; }
    input, textarea { width: 100%; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; }
    textarea { min-height: 100px; resize: vertical; }
    button { background: #6c5ce7; color: white; padding: 0.75rem 1.5rem; border: none; border-radius: 4px; cursor: pointer; }
    button:hover { background: #5b4cdb; }
    .error { color: #dc3545; font-size: 0.875rem; margin-top: 0.25rem; }
  </style>
</head>
<body>
  <h1>Contact Us</h1>
  
  <form action="https://api.formsubmits.com/api/submissions/YOUR_FORM_ID" method="POST">
    <div class="form-group">
      <label for="name">Name *</label>
      <input type="text" id="name" name="name" required>
    </div>
    
    <div class="form-group">
      <label for="email">Email *</label>
      <input type="email" id="email" name="email" required>
    </div>
    
    <div class="form-group">
      <label for="message">Message *</label>
      <textarea id="message" name="message" required></textarea>
    </div>
    
    <button type="submit">Send Message</button>
  </form>
</body>
</html>

Custom Redirect URLs

You can configure custom success redirect URLs in your form settings. This lets you send users to a "Thank You" page.

Step 5
Paid Plans

Advanced: reCAPTCHA & File Uploads

For forms that need extra protection against bots or accept file uploads, here's a complete example with reCAPTCHA Enterprise, honeypot fields, and file handling.

Complete Example: Job Application Form

reCAPTCHA + Files + Honeypot
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Job Application</title>
  
  <!-- reCAPTCHA Enterprise Script -->
  <script src="https://www.google.com/recaptcha/enterprise.js?render=YOUR_RECAPTCHA_SITE_KEY" async defer></script>
  
  <style>
    body { font-family: system-ui, sans-serif; max-width: 600px; margin: 2rem auto; padding: 0 1rem; }
    .form-group { margin-bottom: 1rem; }
    label { display: block; margin-bottom: 0.25rem; font-weight: 500; }
    input, textarea, select { width: 100%; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }
    input[type="file"] { padding: 0.25rem; }
    input.error, textarea.error { border-color: #dc3545; }
    textarea { min-height: 100px; resize: vertical; }
    button { background: #6c5ce7; color: white; padding: 0.75rem 1.5rem; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; }
    button:hover { background: #5b4cdb; }
    button:disabled { background: #ccc; cursor: not-allowed; }
    .field-error { color: #dc3545; font-size: 0.875rem; margin-top: 0.25rem; display: none; }
    .field-error.show { display: block; }
    .message { padding: 1rem; border-radius: 4px; margin-bottom: 1rem; display: none; }
    .message.show { display: block; }
    .message.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
    .message.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
    .file-info { font-size: 0.875rem; color: #666; margin-top: 0.25rem; }
    .honeypot { position: absolute; left: -9999px; }
    .recaptcha-notice { font-size: 0.75rem; color: #666; margin-top: 1rem; text-align: center; }
    .recaptcha-notice a { color: #6c5ce7; }
    .grecaptcha-badge { visibility: hidden !important; }
  </style>
</head>
<body>
  <h1>Job Application</h1>
  
  <div id="successMessage" class="message success"></div>
  <div id="errorMessage" class="message error"></div>
  
  <form id="applicationForm" enctype="multipart/form-data">
    <!-- Honeypot field - invisible to users, catches bots -->
    <input type="text" name="website" class="honeypot" tabindex="-1" autocomplete="off">
    
    <div class="form-group">
      <label for="fullName">Full Name *</label>
      <input type="text" id="fullName" name="fullName" required>
      <div class="field-error" data-field="fullName"></div>
    </div>
    
    <div class="form-group">
      <label for="email">Email *</label>
      <input type="email" id="email" name="email" required>
      <div class="field-error" data-field="email"></div>
    </div>
    
    <div class="form-group">
      <label for="phone">Phone</label>
      <input type="tel" id="phone" name="phone">
      <div class="field-error" data-field="phone"></div>
    </div>
    
    <div class="form-group">
      <label for="position">Position *</label>
      <select id="position" name="position" required>
        <option value="">Select a position...</option>
        <option value="frontend">Frontend Developer</option>
        <option value="backend">Backend Developer</option>
        <option value="fullstack">Full Stack Developer</option>
        <option value="designer">UI/UX Designer</option>
      </select>
      <div class="field-error" data-field="position"></div>
    </div>
    
    <div class="form-group">
      <label for="resume">Resume (PDF, DOC, DOCX) *</label>
      <input type="file" id="resume" name="resume" accept=".pdf,.doc,.docx" required>
      <div class="file-info">Max file size: 10MB</div>
      <div class="field-error" data-field="resume"></div>
    </div>
    
    <div class="form-group">
      <label for="portfolio">Portfolio (optional)</label>
      <input type="file" id="portfolio" name="portfolio" accept=".pdf,.zip">
      <div class="file-info">PDF or ZIP, max 10MB</div>
      <div class="field-error" data-field="portfolio"></div>
    </div>
    
    <div class="form-group">
      <label for="coverLetter">Cover Letter</label>
      <textarea id="coverLetter" name="coverLetter" placeholder="Tell us why you'd be a great fit..."></textarea>
      <div class="field-error" data-field="coverLetter"></div>
    </div>
    
    <!-- Hidden field for reCAPTCHA token -->
    <input type="hidden" id="recaptchaToken" name="g-recaptcha-response">
    <div class="field-error" data-field="g-recaptcha-response"></div>
    
    <button type="submit" id="submitBtn">Submit Application</button>
    
    <!-- reCAPTCHA Notice - REQUIRED when hiding the badge -->
    <p class="recaptcha-notice">
      This site is protected by reCAPTCHA Enterprise and the Google
      <a href="https://policies.google.com/privacy" target="_blank">Privacy Policy</a> and
      <a href="https://policies.google.com/terms" target="_blank">Terms of Service</a> apply.
    </p>
  </form>

  <!-- FormSubmits Helper Library (optional but recommended) -->
  <script src="https://api.formsubmits.com/api/scripts/formsubmits-core.js"></script>
  
  <script>
    const FORM_ID = 'YOUR_FORM_ID';
    const RECAPTCHA_SITE_KEY = 'YOUR_RECAPTCHA_SITE_KEY';
    const RECAPTCHA_ACTION = 'job_application';
    const API_URL = `https://api.formsubmits.com/api/submissions/${FORM_ID}`;
    
    const form = document.getElementById('applicationForm');
    const submitBtn = document.getElementById('submitBtn');
    const successMessage = document.getElementById('successMessage');
    const errorMessage = document.getElementById('errorMessage');
    const recaptchaInput = document.getElementById('recaptchaToken');
    
    // Initialize FormSubmits handler (if using helper library)
    let formHandler;
    if (typeof FormSubmissionHandler !== 'undefined') {
      formHandler = new FormSubmissionHandler(form, {
        onSuccess: (response) => console.log('Success!', response),
        onError: (response) => console.log('Error:', response),
        resetOnSuccess: true
      });
      formHandler.enableAutoClearErrors();
    }
    
    // Get reCAPTCHA token
    async function getRecaptchaToken() {
      return new Promise((resolve, reject) => {
        if (typeof grecaptcha === 'undefined' || typeof grecaptcha.enterprise === 'undefined') {
          reject(new Error('reCAPTCHA not loaded. Please refresh the page.'));
          return;
        }
        
        grecaptcha.enterprise.ready(async () => {
          try {
            const token = await grecaptcha.enterprise.execute(RECAPTCHA_SITE_KEY, { action: RECAPTCHA_ACTION });
            resolve(token);
          } catch (error) {
            reject(error);
          }
        });
      });
    }
    
    // Clear all errors
    function clearErrors() {
      document.querySelectorAll('.field-error').forEach(el => {
        el.classList.remove('show');
        el.textContent = '';
      });
      document.querySelectorAll('input.error, textarea.error, select.error').forEach(el => {
        el.classList.remove('error');
      });
      successMessage.classList.remove('show');
      errorMessage.classList.remove('show');
    }
    
    // Display field errors
    function displayErrors(errors) {
      errors.forEach(err => {
        const errorEl = document.querySelector(`[data-field="${err.name}"]`);
        const inputEl = document.querySelector(`[name="${err.name}"]`);
        
        if (errorEl) {
          errorEl.textContent = err.errors.join('. ');
          errorEl.classList.add('show');
        }
        if (inputEl) {
          inputEl.classList.add('error');
        }
      });
    }
    
    form.addEventListener('submit', async function(e) {
      e.preventDefault();
      clearErrors();
      
      submitBtn.disabled = true;
      submitBtn.textContent = 'Verifying...';
      
      try {
        // Get reCAPTCHA token first
        const token = await getRecaptchaToken();
        recaptchaInput.value = token;
        
        submitBtn.textContent = 'Submitting...';
        
        // Create FormData (required for file uploads)
        const formData = new FormData(form);
        
        const response = await fetch(API_URL, {
          method: 'POST',
          body: formData,
          headers: {
            'X-Requested-With': 'XMLHttpRequest'
          }
        });
        
        const result = await response.json();
        
        if (result.code === 0) {
          // Success
          const message = result.data[0]?.message || 'Your application has been submitted!';
          const requestId = result.data[0]?.submission?.requestId;
          successMessage.innerHTML = message + (requestId ? `<br><small>Reference: ${requestId}</small>` : '');
          successMessage.classList.add('show');
          form.reset();
        } else {
          // Validation errors
          if (result.errors && result.errors.length > 0) {
            displayErrors(result.errors);
          }
          errorMessage.textContent = 'Please fix the errors and try again.';
          errorMessage.classList.add('show');
        }
      } catch (error) {
        console.error('Submission error:', error);
        errorMessage.textContent = error.message || 'An error occurred. Please try again.';
        errorMessage.classList.add('show');
      } finally {
        submitBtn.disabled = false;
        submitBtn.textContent = 'Submit Application';
      }
    });
  </script>
</body>
</html>

Using the Helper Script

The example above includes our optional helper script formsubmits-core.js which simplifies error handling and form state management. See the Helper Script Documentation below for details.

Step 6

Test Your Form

Now it's time to test everything! Submit your form and verify that the data arrives correctly in the FormSubmits on the Submissions page.

Testing Checklist:

  • Open your form page

    Can be on localhost, staging, or production

  • Fill out the form with test data

    Use a real email if you want to test notifications

  • Submit the form

    Check for success message or redirect with ?submission=success

  • Go to Submissions in the dashboard

    Your submission should appear in the list

  • Click on the submission to view details

    Verify all field data was captured correctly

Submission Details

In the submission details view, you can see all submitted data, uploaded files, submission timestamp, IP address, user agent, and any validation or processing notes. You can also add internal notes for your team.

Understanding Submission Results

Submissions can either be accepted, rejected, or marked as spam. Here's how each works:

Unknown Fields Are Accepted by Default

By default, forms accept all submitted fields — even those not listed in your configuration. Only fields with validation rules are checked. You can change this behavior in form settings to accept without checking or ignore unknown fields.

Debugging with AJAX Submissions

When submitting via AJAX, the API returns detailed error information for each field that failed validation. Use this to display helpful error messages to your users so they can correct their input and resubmit.

🎉 Congratulations!

Your form is now live and collecting validated submissions. You're all set!

Reference

Helper Script Documentation

The formsubmits-core.js helper script simplifies AJAX form submissions by handling error display, loading states, and success messages automatically.

Script URL:

https://api.formsubmits.com/api/scripts/formsubmits-core.js
<!-- Include the script -->
<script src="https://api.formsubmits.com/api/scripts/formsubmits-core.js"></script>

<script>
  const form = document.getElementById('myForm');
  
  const handler = new FormSubmissionHandler(form, {
    // Callbacks
    onSuccess: (response) => {
      console.log('Form submitted!', response);
    },
    onError: (response) => {
      console.log('Validation errors:', response.errors);
    },
    onBeforeSubmit: () => {
      console.log('Submitting...');
    },
    onAfterSubmit: () => {
      console.log('Done!');
    },
    
    // Options
    autoScroll: true,        // Scroll to messages (default: true)
    resetOnSuccess: true,    // Clear form after success (default: true)
    invalidClass: 'is-invalid', // Class for invalid inputs
    showClass: 'show',       // Class to show error elements
    
    // Selectors (customize if your HTML differs)
    errorSummarySelector: '.error-summary',
    errorSummaryListSelector: '#errorSummaryList',
    fieldErrorSelector: '[data-field]',
    successMessageSelector: '.success-message',
    successMessageTextSelector: '#successMessageText'
  });
  
  // Enable auto-clearing of field errors when user types
  handler.enableAutoClearErrors();
</script>

jQuery Support

If jQuery is available on your page, you can also use the jQuery plugin syntax:

$('#myForm').formSubmissionHandler({ onSuccess: ... });

Dependencies: The script works with native fetch() API or Axios if available. No other dependencies required.

Ready to Get Started?

Create your free account and start collecting form submissions in minutes.