diff --git a/config/routes.rb b/config/routes.rb
index a9e383cc..3d10d04d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -34,6 +34,7 @@ Rails.application.routes.draw do
     resources :submitter_form_views, only: %i[create]
     resources :submitters, only: %i[index show update]
     resources :submissions, only: %i[index show create destroy] do
+      resources :documents, only: %i[index], controller: 'submission_documents'
       collection do
         resources :init, only: %i[create], controller: 'submissions'
         resources :emails, only: %i[create], controller: 'submissions', as: :submissions_emails
diff --git a/lib/accounts.rb b/lib/accounts.rb
index d6088bab..644032b1 100644
--- a/lib/accounts.rb
+++ b/lib/accounts.rb
@@ -11,7 +11,7 @@ module Accounts
     new_user.uuid = SecureRandom.uuid
     new_user.account = new_account
     new_user.encrypted_password = SecureRandom.hex
-    new_user.email = "#{SecureRandom.hex}@docuseal.co"
+    new_user.email = "#{SecureRandom.hex}@docuseal.com"
 
     account.templates.each do |template|
       new_template = template.dup
diff --git a/lib/action_mailer_configs_interceptor.rb b/lib/action_mailer_configs_interceptor.rb
index b868fc27..0eeddfa7 100644
--- a/lib/action_mailer_configs_interceptor.rb
+++ b/lib/action_mailer_configs_interceptor.rb
@@ -16,7 +16,15 @@ module ActionMailerConfigsInterceptor
     end
 
     if Rails.env.production? && Rails.application.config.action_mailer.delivery_method
-      message.from = ENV.fetch('SMTP_FROM')
+      from = ENV.fetch('SMTP_FROM').to_s.split(',').sample
+
+      message.from = from
+
+      if from == 'DocuSeal '
+        message.body.instance_variable_set(
+          :@raw_source, message.body.raw_source.gsub('https://docuseal.co/', 'https://docuseal.com/')
+        )
+      end
 
       return message
     end
diff --git a/lib/action_mailer_events_observer.rb b/lib/action_mailer_events_observer.rb
index c7786bda..273564ac 100644
--- a/lib/action_mailer_events_observer.rb
+++ b/lib/action_mailer_events_observer.rb
@@ -22,7 +22,7 @@ module ActionMailerEventsObserver
         emailable_type:,
         event_type: :send,
         email:,
-        data: { method: mail.delivery_method.class.name.underscore },
+        data: { from: mail.from, method: mail.delivery_method.class.name.underscore },
         event_datetime: Time.current
       )
     end
diff --git a/lib/docuseal.rb b/lib/docuseal.rb
index 2b3e283d..9ea6f35e 100644
--- a/lib/docuseal.rb
+++ b/lib/docuseal.rb
@@ -12,7 +12,7 @@ module Docuseal
   TWITTER_URL = 'https://twitter.com/docusealco'
   TWITTER_HANDLE = '@docusealco'
   CHATGPT_URL = 'https://chatgpt.com/g/g-9hg8AAw0r-docuseal'
-  SUPPORT_EMAIL = 'support@docuseal.co'
+  SUPPORT_EMAIL = 'support@docuseal.com'
   HOST = ENV.fetch('HOST', 'localhost')
   AATL_CERT_NAME = 'docuseal_aatl'
   CONSOLE_URL = if Rails.env.development?
diff --git a/lib/params/base_validator.rb b/lib/params/base_validator.rb
index cc037300..4c2571e8 100644
--- a/lib/params/base_validator.rb
+++ b/lib/params/base_validator.rb
@@ -2,6 +2,8 @@
 
 module Params
   class BaseValidator
+    EMAIL_REGEXP = /\A[a-z0-9][\.']?(?:(?:[a-z0-9_-]+[\.\+'])*[a-z0-9_-]+)*@(?:[a-z0-9]+[\.-])*[a-z0-9]+\.[a-z]{2,}\z/i
+
     InvalidParameterError = Class.new(StandardError)
 
     def self.call(...)
@@ -65,6 +67,27 @@ module Params
       raise_error(message || "#{key} must follow the #{regexp.source} format")
     end
 
+    def email_format(params, key, message: nil)
+      return if params.blank?
+      return if params[key].blank?
+      return if params[key].to_s.strip.split(/\s*[;,]\s*/).all? { |email| email.match?(EMAIL_REGEXP) }
+
+      if Rails.env.production?
+        Rollbar.error(message || "#{key} must follow the email format") if defined?(Rollbar)
+      else
+        raise_error(message || "#{key} must follow the email format")
+      end
+    end
+
+    def unique_value(params, key, message: nil)
+      return unless params.is_a?(Array)
+      return if params.none?
+      return if params.all? { |p| p[key].blank? }
+      return if params.pluck(key).compact_blank.uniq.size == params.pluck(key).compact_blank.size
+
+      raise_error(message || "#{key} must be unique")
+    end
+
     def in_path(params, path = [])
       old_path = @current_path
 
diff --git a/lib/params/submission_create_validator.rb b/lib/params/submission_create_validator.rb
index 7ea76401..eef14391 100644
--- a/lib/params/submission_create_validator.rb
+++ b/lib/params/submission_create_validator.rb
@@ -22,6 +22,7 @@ module Params
       required(params, %i[emails email])
 
       type(params, :emails, String)
+      email_format(params, :emails, message: 'emails are invalid')
       boolean(params, :send_email)
       type(params, :message, Hash)
 
@@ -43,8 +44,8 @@ module Params
       type(params, :completed_redirect_url, String)
       type(params, :bcc_completed, String)
       type(params, :reply_to, String)
-      format(params, :bcc_completed, /@/, message: 'bcc_completed email is invalid')
-      format(params, :reply_to, /@/, message: 'reply_to email is invalid')
+      email_format(params, :bcc_completed, message: 'bcc_completed email is invalid')
+      email_format(params, :reply_to, message: 'reply_to email is invalid')
       type(params, :message, Hash)
       type(params, :submitters, Array)
 
@@ -60,6 +61,7 @@ module Params
       if params[:submitters].present?
         in_path(params, :submitters) do |submitters_params|
           type(submitters_params, 0, Hash)
+          unique_value(submitters_params, :role)
         end
       end
 
@@ -74,8 +76,8 @@ module Params
       type(submitter_params, :name, String)
       type(submitter_params, :reply_to, String)
       type(submitter_params, :email, String)
-      format(submitter_params, :email, /@/, message: 'email is invalid')
-      format(submitter_params, :reply_to, /@/, message: 'reply_to email is invalid')
+      email_format(submitter_params, :email, message: 'email is invalid')
+      email_format(submitter_params, :reply_to, message: 'reply_to email is invalid')
       type(submitter_params, :phone, String)
       format(submitter_params, :phone, /\A\+\d+\z/,
              message: 'phone should start with + and contain only digits')
@@ -105,7 +107,7 @@ module Params
       type(params, :order, String)
       type(params, :completed_redirect_url, String)
       type(params, :bcc_completed, String)
-      format(params, :bcc_completed, /@/, message: 'bcc_completed email is invalid')
+      email_format(params, :bcc_completed, message: 'bcc_completed email is invalid')
       type(params, :message, Hash)
 
       in_path(params, :message) do |message_params|
diff --git a/lib/submissions/generate_preview_attachments.rb b/lib/submissions/generate_preview_attachments.rb
new file mode 100644
index 00000000..94c836c3
--- /dev/null
+++ b/lib/submissions/generate_preview_attachments.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+module Submissions
+  module GeneratePreviewAttachments
+    module_function
+
+    # rubocop:disable Metrics
+    def call(submission, values_hash: nil)
+      values_hash ||= build_values_hash(submission)
+
+      with_signature_id = submission.account.account_configs
+                                    .exists?(key: AccountConfig::WITH_SIGNATURE_ID, value: true)
+
+      is_flatten =
+        submission.account.account_configs
+                  .find_or_initialize_by(key: AccountConfig::FLATTEN_RESULT_PDF_KEY).value != false
+
+      pdfs_index = GenerateResultAttachments.build_pdfs_index(submission, flatten: is_flatten)
+
+      submission.submitters.where(completed_at: nil).preload(attachments_attachments: :blob).each do |submitter|
+        GenerateResultAttachments.fill_submitter_fields(submitter, submission.account, pdfs_index,
+                                                        with_signature_id:, is_flatten:)
+      end
+
+      template = submission.template
+
+      image_pdfs = []
+      original_documents = template.documents.preload(:blob)
+
+      result_attachments =
+        (submission.template_schema || template.schema).map do |item|
+          pdf = pdfs_index[item['attachment_uuid']]
+
+          if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
+            pdf = GenerateResultAttachments.normalize_image_pdf(pdf)
+
+            image_pdfs << pdf
+          end
+
+          build_pdf_attachment(pdf:, submission:,
+                               uuid: item['attachment_uuid'],
+                               values_hash:,
+                               name: item['name'])
+        end
+
+      return ApplicationRecord.no_touching { result_attachments.map { |e| e.tap(&:save!) } } if image_pdfs.size < 2
+
+      images_pdf =
+        image_pdfs.each_with_object(HexaPDF::Document.new) do |pdf, doc|
+          pdf.pages.each { |page| doc.pages << doc.import(page) }
+        end
+
+      images_pdf = GenerateResultAttachments.normalize_image_pdf(images_pdf)
+
+      images_pdf_attachment =
+        build_pdf_attachment(
+          pdf: images_pdf,
+          submission:,
+          uuid: GenerateResultAttachments.images_pdf_uuid(original_documents.select(&:image?)),
+          values_hash:,
+          name: template.name
+        )
+
+      ApplicationRecord.no_touching do
+        (result_attachments + [images_pdf_attachment]).map { |e| e.tap(&:save!) }
+      end
+    end
+
+    def build_values_hash(submission)
+      submission.submitters.reduce({}) { |acc, s| acc.merge(s.values) }.hash
+    end
+
+    def build_pdf_attachment(pdf:, submission:, uuid:, name:, values_hash:)
+      io = StringIO.new
+
+      begin
+        pdf.write(io, incremental: true, validate: false)
+      rescue HexaPDF::MalformedPDFError => e
+        Rollbar.error(e) if defined?(Rollbar)
+
+        pdf.write(io, incremental: false, validate: false)
+      end
+
+      ActiveStorage::Attachment.new(
+        blob: ActiveStorage::Blob.create_and_upload!(io: io.tap(&:rewind), filename: "#{name}.pdf"),
+        metadata: { original_uuid: uuid,
+                    values_hash:,
+                    analyzed: true,
+                    sha256: Base64.urlsafe_encode64(Digest::SHA256.digest(io.string)) },
+        name: 'preview_documents',
+        record: submission
+      )
+    end
+    # rubocop:enable Metrics
+  end
+end
diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb
index c396ba31..f1ef7fb7 100644
--- a/lib/submissions/generate_result_attachments.rb
+++ b/lib/submissions/generate_result_attachments.rb
@@ -92,19 +92,13 @@ module Submissions
     end
 
     def generate_pdfs(submitter)
-      cell_layouter = HexaPDF::Layout::TextLayouter.new(text_valign: :center, text_align: :center)
-
-      with_signature_id = submitter.account.account_configs
-                                   .exists?(key: AccountConfig::WITH_SIGNATURE_ID, value: true)
-
-      is_flatten =
-        submitter.account.account_configs
-                 .find_or_initialize_by(key: AccountConfig::FLATTEN_RESULT_PDF_KEY).value != false
+      configs = submitter.account.account_configs.where(key: [AccountConfig::FLATTEN_RESULT_PDF_KEY,
+                                                              AccountConfig::WITH_SIGNATURE_ID])
 
-      account = submitter.account
-      attachments_data_cache = {}
+      with_signature_id = configs.find { |c| c.key == AccountConfig::WITH_SIGNATURE_ID }&.value == true
+      is_flatten = configs.find { |c| c.key == AccountConfig::FLATTEN_RESULT_PDF_KEY }&.value != false
 
-      pdfs_index = build_pdfs_index(submitter, flatten: is_flatten)
+      pdfs_index = build_pdfs_index(submitter.submission, submitter:, flatten: is_flatten)
 
       if with_signature_id || submitter.account.testing?
         pdfs_index.each_value do |pdf|
@@ -145,6 +139,16 @@ module Submissions
         end
       end
 
+      fill_submitter_fields(submitter, submitter.account, pdfs_index, with_signature_id:, is_flatten:)
+    end
+
+    def fill_submitter_fields(submitter, account, pdfs_index, with_signature_id:, is_flatten:)
+      cell_layouter = HexaPDF::Layout::TextLayouter.new(text_valign: :center, text_align: :center)
+
+      attachments_data_cache = {}
+
+      return pdfs_index if submitter.submission.template_fields.blank?
+
       submitter.submission.template_fields.each do |field|
         next if field['submitter_uuid'] != submitter.uuid
 
@@ -514,13 +518,13 @@ module Submissions
       Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, attachments.map(&:uuid).sort.join(':'))
     end
 
-    def build_pdfs_index(submitter, flatten: true)
-      latest_submitter = find_last_submitter(submitter)
+    def build_pdfs_index(submission, submitter: nil, flatten: true)
+      latest_submitter = find_last_submitter(submission, submitter:)
 
       Submissions::EnsureResultGenerated.call(latest_submitter) if latest_submitter
 
       documents   = latest_submitter&.documents&.preload(:blob).to_a.presence
-      documents ||= submitter.submission.template_schema_documents.preload(:blob)
+      documents ||= submission.template_schema_documents.preload(:blob)
 
       documents.to_h do |attachment|
         pdf =
@@ -582,11 +586,11 @@ module Submissions
       font_wrapper.custom_glyph(replace_with, character)
     end
 
-    def find_last_submitter(submitter)
-      submitter.submission.submitters
-               .select(&:completed_at?)
-               .select { |e| e.id != submitter.id && e.completed_at <= submitter.completed_at }
-               .max_by(&:completed_at)
+    def find_last_submitter(submission, submitter: nil)
+      submission.submitters
+                .select(&:completed_at?)
+                .select { |e| submitter.nil? ? true : e.id != submitter.id && e.completed_at <= submitter.completed_at }
+                .max_by(&:completed_at)
     end
 
     def build_pdf_from_image(attachment)
diff --git a/spec/factories/templates.rb b/spec/factories/templates.rb
index d1c433d2..f4f2ff62 100644
--- a/spec/factories/templates.rb
+++ b/spec/factories/templates.rb
@@ -7,7 +7,11 @@ FactoryBot.define do
     author factory: %i[user]
     name { Faker::Book.title }
 
-    after(:create) do |template|
+    transient do
+      submitter_count { 1 }
+    end
+
+    after(:create) do |template, evaluator|
       blob = ActiveStorage::Blob.create_and_upload!(
         io: Rails.root.join('spec/fixtures/sample-document.pdf').open,
         filename: 'sample-document.pdf',
@@ -22,39 +26,46 @@ FactoryBot.define do
       Templates::ProcessDocument.call(attachment, attachment.download)
 
       template.schema = [{ attachment_uuid: attachment.uuid, name: 'sample-document' }]
-      template.submitters = [
-        {
-          'name' => 'First Party',
-          'uuid' => '513848eb-1096-4abc-a743-68596b5aaa4c'
-        }
-      ]
-      template.fields = [
-        {
-          'uuid' => '21637fc9-0655-45df-8952-04ec64949e85',
-          'submitter_uuid' => '513848eb-1096-4abc-a743-68596b5aaa4c',
-          'name' => 'First Name',
-          'type' => 'text',
-          'required' => true,
-          'areas' => [
-            {
-              'x' => 0.09027777777777778,
-              'y' => 0.1197252208047105,
-              'w' => 0.3069444444444444,
-              'h' => 0.03336604514229637,
-              'attachment_uuid' => attachment.uuid,
-              'page' => 0
-            }
-          ]
-        },
+      number_words = %w[first second third fourth fifth sixth seventh eighth ninth tenth]
+
+      template.submitters = Array.new(evaluator.submitter_count) do |i|
         {
-          'uuid' => '1f97f8e3-dc82-4586-aeea-6ebed6204e46',
-          'submitter_uuid' => '513848eb-1096-4abc-a743-68596b5aaa4c',
-          'name' => '',
-          'type' => 'signature',
-          'required' => true,
-          'areas' => []
+          'name' => "#{number_words[i]&.capitalize} Party",
+          'uuid' => SecureRandom.uuid
         }
-      ]
+      end
+
+      template.fields = template.submitters.reduce([]) do |fields, submitter|
+        fields += [
+          {
+            'uuid' => SecureRandom.uuid,
+            'submitter_uuid' => submitter['uuid'],
+            'name' => 'First Name',
+            'type' => 'text',
+            'required' => true,
+            'areas' => [
+              {
+                'x' => 0.09027777777777778,
+                'y' => 0.1197252208047105,
+                'w' => 0.3069444444444444,
+                'h' => 0.03336604514229637,
+                'attachment_uuid' => attachment.uuid,
+                'page' => 0
+              }
+            ]
+          },
+          {
+            'uuid' => SecureRandom.uuid,
+            'submitter_uuid' => submitter['uuid'],
+            'name' => '',
+            'type' => 'signature',
+            'required' => true,
+            'areas' => []
+          }
+        ]
+
+        fields
+      end
 
       template.save!
     end
diff --git a/spec/lib/params/base_validator_spec.rb b/spec/lib/params/base_validator_spec.rb
new file mode 100644
index 00000000..193b9b33
--- /dev/null
+++ b/spec/lib/params/base_validator_spec.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Params::BaseValidator do
+  let(:validator) { described_class.new({}) }
+
+  describe '#email_format' do
+    it 'when email is valid' do
+      emails = [
+        '  john.doe@example.com  ',
+        'john.doe@example.com',
+        'jane+newsletter@domain.org',
+        'mike_smith@company.net',
+        'lisa-wong@sub.example.co.uk',
+        'peter@webmail.com',
+        'anna.jones123@my-domain.com',
+        'contact@company.email',
+        'info@my-company123.org',
+        'hello.world@business.info',
+        'feedback@new-domain.com',
+        'alerts+user@localdomain.net',
+        'webmaster@industry.biz',
+        'services@agency.example',
+        'george123@consultant.pro',
+        'sales-team@company.io'
+      ]
+
+      emails.each do |email|
+        expect { validator.email_format({ email: }, :email) }.not_to raise_error
+      end
+    end
+
+    it 'when signle email is invalid' do
+      emails = [
+        'jone.doe@',
+        'mike.smith@',
+        'jane.doe@@example.com',
+        '@example.com',
+        'lisa.wong@example',
+        'peter.parker..@example.com',
+        'anna.jones@.com',
+        'jack.brown@com',
+        'john doe@example.com',
+        'laura.martin@ example.com',
+        'dave.clark@example .com',
+        'susan.green@example,com',
+        'chris.lee@example;com',
+        'jenny.king@.example.com',
+        '.henry.ford@example.com',
+        'amy.baker@sub_domain.com',
+        'george.morris@-example.com',
+        'nancy.davis@example..com',
+        'kevin.white@.',
+        'diana.robinson@.example..com',
+        'oliver.scott@example.c',
+        'email1@g.comemail@g.com',
+        'user.name@subdomain.example@example.com',
+        'double@at@sign.com',
+        'user@@example.com',
+        'email@123.123.123.123',
+        'this...is@strange.but.valid.com',
+        'mix-and.match@strangely-formed-email_address.com',
+        'email@domain..com',
+        'user@-weird-domain-.com',
+        'user.name@[IPv6:2001:db8::1]',
+        'tricky.email@sub.example-.com',
+        'user@domain.c0m'
+      ]
+
+      emails.each do |email|
+        expect do
+          validator.email_format({ email: }, :email)
+        end.to raise_error(described_class::InvalidParameterError, 'email must follow the email format')
+      end
+    end
+
+    it 'when multiple emails are valid' do
+      emails = [
+
+        'john.doe@example.com, jane.doe+newsletter@domain.org',
+        'joshua@automobile.car ; chloe+fashion@food.delivery',
+        'mike-smith@company.net;lisa.wong-sales@sub.example.co.uk',
+        'peter.parker+info@webmail.com,laura.martin-office@company.co',
+        'anna.jones123@my-domain.com, jack.brown+work@college.edu',
+        'susan.green@business-info.org; dave.clark+personal@nonprofit.org',
+        'chris.lee+team@new-domain.com;jenny.king.marketing@localdomain.net',
+        'george.morris@consultant.pro; nancy.davis-office@company.io',
+        'joshua-jones@automobile.car; chloe.taylor+fashion@food.delivery',
+        'ryan.moore+alerts@music-band.com,isabella.walker.design@fashion.design',
+        'support-team@company.com, contact.us@domain.org',
+        'admin.office@industry.biz, hr.department@service.pro',
+        'feedback@agency-example.org; hello.world@creative-studio.net',
+        'sales-team@e-commerce.shop, support.department@technology.co',
+        'media.contact@financial.servicesl; events-coordinator@food.delivery',
+        'order@music-band.com; info.support@creative.example',
+        'design.team@webmail.com , admin-office@company.co',
+        'contact.sales@sub-example.co.uk, support+info@legal.gov',
+        'support@media.group;subscribe-updates@concert.events'
+      ]
+
+      emails.each do |email|
+        expect { validator.email_format({ email: }, :email) }.not_to raise_error
+      end
+    end
+
+    it 'when multiple emails are invalid' do
+      emails = [
+        'jone@gmail.com, ,mike@gmail.com',
+        'john.doe@example.com  dave@nonprofit.org',
+        '; oliver.scott@example.com',
+        'amy.baker@ example.com, george.morris@ example.com',
+        'jenny.king@example.com . diana.robinson@example.com',
+        'nancy.davis@.com, henry.ford@.com',
+        'jack.brown@example.com, laura.martin@example .com',
+        'anna.jones@example,com lisa.wong@example.com',
+        'dave.clark@example.com kevin.white@example;com',
+        'susan.green@ example.com; john.doe@example.com',
+        'amy.baker@sub_domain.com george.morris@-example.com',
+        'nancy.davis@example..com john.doe@example.c',
+        'peter.parker@example.com, .henry.ford@example.com',
+        'diana.robinson@.example..com, mike.smith@.',
+        'oliver.scott@example.com; laura.martin@ example.com, jane.doe@@example.com'
+      ]
+
+      emails.each do |email|
+        expect do
+          validator.email_format({ email: }, :email)
+        end.to raise_error(described_class::InvalidParameterError, 'email must follow the email format')
+      end
+    end
+
+    it 'when email is invalid with custom message' do
+      expect do
+        validator.email_format({ email: 'jone.doe@' }, :email, message: 'email is invalid')
+      end.to raise_error(described_class::InvalidParameterError, 'email is invalid')
+    end
+  end
+end
diff --git a/spec/requests/submissions_spec.rb b/spec/requests/submissions_spec.rb
index 81cdb6c2..3a9590d8 100644
--- a/spec/requests/submissions_spec.rb
+++ b/spec/requests/submissions_spec.rb
@@ -7,6 +7,7 @@ describe 'Submission API', type: :request do
   let!(:author) { create(:user, account:) }
   let!(:folder) { create(:template_folder, account:) }
   let!(:templates) { create_list(:template, 2, account:, author:, folder:) }
+  let!(:multiple_submitters_template) { create(:template, submitter_count: 3, account:, author:, folder:) }
 
   describe 'GET /api/submissions' do
     it 'returns a list of submissions' do
@@ -56,20 +57,98 @@ describe 'Submission API', type: :request do
 
       expect(response.parsed_body).to eq(JSON.parse(create_submission_body(submission).to_json))
     end
+
+    it 'creates a submission when some submitter roles are not provided' do
+      post '/api/submissions', headers: { 'x-auth-token': author.access_token.token }, params: {
+        template_id: multiple_submitters_template.id,
+        send_email: true,
+        submitters: [
+          { role: 'First Role', email: 'john.doe@example.com' },
+          { email: 'jane.doe@example.com' },
+          { email: 'mike.doe@example.com' }
+        ]
+      }.to_json
+
+      expect(response).to have_http_status(:ok)
+
+      submission = Submission.last
+
+      expect(response.parsed_body).to eq(JSON.parse(create_submission_body(submission).to_json))
+      expect(response.parsed_body).to eq(JSON.parse(create_submission_body(submission).to_json))
+      expect(response.parsed_body[0]['role']).to eq('First Party')
+      expect(response.parsed_body[0]['email']).to eq('john.doe@example.com')
+      expect(response.parsed_body[1]['role']).to eq('Second Party')
+      expect(response.parsed_body[1]['email']).to eq('jane.doe@example.com')
+      expect(response.parsed_body[2]['role']).to eq('Third Party')
+      expect(response.parsed_body[2]['email']).to eq('mike.doe@example.com')
+    end
+
+    it 'returns an error if the submitter email is invalid' do
+      post '/api/submissions', headers: { 'x-auth-token': author.access_token.token }, params: {
+        template_id: templates[0].id,
+        send_email: true,
+        submitters: [
+          { role: 'First Role', email: 'john@example' }
+        ]
+      }.to_json
+
+      expect(response).to have_http_status(:unprocessable_entity)
+
+      expect(response.parsed_body).to eq({ 'error' => 'email is invalid in `submitters[0]`.' })
+    end
+
+    it 'returns an error if the template fields are missing' do
+      templates[0].update(fields: [])
+
+      post '/api/submissions', headers: { 'x-auth-token': author.access_token.token }, params: {
+        template_id: templates[0].id,
+        send_email: true,
+        submitters: [{ role: 'First Role', email: 'john.doe@example.com' }]
+      }.to_json
+
+      expect(response).to have_http_status(:unprocessable_entity)
+      expect(response.parsed_body).to eq({ 'error' => 'Template does not contain fields' })
+    end
+
+    it 'returns an error if submitter roles are not unique' do
+      post '/api/submissions', headers: { 'x-auth-token': author.access_token.token }, params: {
+        template_id: multiple_submitters_template.id,
+        send_email: true,
+        submitters: [
+          { role: 'First Role', email: 'john.doe@example.com' },
+          { role: 'First Role', email: 'jane.doe@example.com' }
+        ]
+      }.to_json
+
+      expect(response).to have_http_status(:unprocessable_entity)
+      expect(response.parsed_body).to eq({ 'error' => 'role must be unique in `submitters`.' })
+    end
   end
 
   describe 'POST /api/submissions/emails' do
     it 'creates a submission using email' do
-      post '/api/submissions', headers: { 'x-auth-token': author.access_token.token }, params: {
+      post '/api/submissions/emails', headers: { 'x-auth-token': author.access_token.token }, params: {
         template_id: templates[0].id,
-        emails: 'john.doe@example.com'
+        emails: 'john.doe@example.com,jane.doe@example.com'
       }.to_json
 
       expect(response).to have_http_status(:ok)
 
-      submission = Submission.last
+      submissions = Submission.last(2)
+      submissions_body = submissions.reduce([]) { |acc, submission| acc + create_submission_body(submission) }
 
-      expect(response.parsed_body).to eq(JSON.parse(create_submission_body(submission).to_json))
+      expect(response.parsed_body).to eq(JSON.parse(submissions_body.to_json))
+    end
+
+    it 'returns an error if emails are invalid' do
+      post '/api/submissions/emails', headers: { 'x-auth-token': author.access_token.token }, params: {
+        template_id: templates[0].id,
+        emails: 'amy.baker@example.com, george.morris@example.com@gmail.com'
+      }.to_json
+
+      expect(response).to have_http_status(:unprocessable_entity)
+
+      expect(response.parsed_body).to eq({ 'error' => 'emails are invalid' })
     end
   end
 
diff --git a/spec/requests/templates_spec.rb b/spec/requests/templates_spec.rb
index aa4951bf..67430ed8 100644
--- a/spec/requests/templates_spec.rb
+++ b/spec/requests/templates_spec.rb
@@ -144,8 +144,8 @@ describe 'Templates API', type: :request do
       name: template.name,
       fields: [
         {
-          'uuid' => '21637fc9-0655-45df-8952-04ec64949e85',
-          'submitter_uuid' => '513848eb-1096-4abc-a743-68596b5aaa4c',
+          'uuid' => template.fields[0]['uuid'],
+          'submitter_uuid' => template.submitters[0]['uuid'],
           'name' => 'First Name',
           'type' => 'text',
           'required' => true,
@@ -161,8 +161,8 @@ describe 'Templates API', type: :request do
           ]
         },
         {
-          'uuid' => '1f97f8e3-dc82-4586-aeea-6ebed6204e46',
-          'submitter_uuid' => '513848eb-1096-4abc-a743-68596b5aaa4c',
+          'uuid' => template.fields[1]['uuid'],
+          'submitter_uuid' => template.submitters[0]['uuid'],
           'name' => '',
           'type' => 'signature',
           'required' => true,