diff --git a/app/controllers/email_smtp_settings_controller.rb b/app/controllers/email_smtp_settings_controller.rb
index 766a9545..972765f3 100644
--- a/app/controllers/email_smtp_settings_controller.rb
+++ b/app/controllers/email_smtp_settings_controller.rb
@@ -1,6 +1,7 @@
 # frozen_string_literal: true
 
 class EmailSmtpSettingsController < ApplicationController
+  skip_before_action :verify_authenticity_token
   before_action :load_encrypted_config
   authorize_resource :encrypted_config, only: :index
   authorize_resource :encrypted_config, parent: false, only: :create
diff --git a/app/controllers/export_controller.rb b/app/controllers/export_controller.rb
index cfa94cf4..cf2c1da2 100644
--- a/app/controllers/export_controller.rb
+++ b/app/controllers/export_controller.rb
@@ -7,56 +7,28 @@ class ExportController < ApplicationController
   skip_before_action :maybe_redirect_to_setup
   skip_before_action :verify_authenticity_token
 
-  # Template is sent as JSON already; we're just gonnna send it on to the third party.
+  # Send template to third party.
   def export_template
-    export_location = ExportLocation.default_location
-
     data = request.raw_post.present? ? JSON.parse(request.raw_post) : params.to_unsafe_h
-    response = post_to_api(data, export_location.templates_endpoint, export_location.extra_params)
+    service = ExportTemplateService.new(data)
 
-    if response&.success?
-      Rails.logger.info("Successfully exported template #{data[:template][:name]} to #{export_location.name}")
+    if service.call
       head :ok
     else
-      Rails.logger.error("Failed to export template to third party: #{response&.status}")
-      Rollbar.error("#{export_location.name} template export API error: #{response&.status}") if defined?(Rollbar)
-      head :unprocessable_entity
+      redirect_to root_path, alert: service.error_message
     end
-  rescue Faraday::Error => e
-    Rails.logger.error("Failed to export template Faraday: #{e.message}")
-    Rollbar.error("Failed to export template: #{e.message}") if defined?(Rollbar)
-    head :service_unavailable
-  rescue StandardError => e
-    Rails.logger.error("Failed to export template: #{e.message}")
-    Rollbar.error(e) if defined?(Rollbar)
-    head :internal_server_error
   end
 
-  private
+  # Send submission to third party.
+  def export_submission
+    submission = Submission.find(params[:id])
+    service = ExportSubmissionService.new(submission)
 
-  def api_connection
-    @api_connection ||= Faraday.new(url: ExportLocation.default_location.api_base_url) do |faraday|
-      faraday.request :json
-      faraday.response :json
-      faraday.adapter Faraday.default_adapter
+    if service.call
+      redirect_to submission, notice: "Submission ##{submission.id} events exported successfully."
+    else
+      redirect_to submission, alert: service.error_message
     end
-  rescue StandardError => e
-    Rails.logger.error("Failed to create API connection: #{e.message}")
-    Rollbar.error(e) if defined?(Rollbar)
-    nil
   end
 
-  def post_to_api(data, endpoint, extra_params = nil)
-    connection = api_connection
-    return nil unless connection
-
-    connection.post(endpoint) do |req|
-      # req.headers['Authorization'] = "Bearer #{export_location.authorization_token}" lol
-
-      # Merge extra_params into data if provided
-      data = data.merge(extra_params) if extra_params.present? && data.is_a?(Hash)
-
-      req.body = data.is_a?(String) ? data : data.to_json
-    end
-  end
 end
diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb
index c411b5df..389e9591 100644
--- a/app/controllers/submissions_controller.rb
+++ b/app/controllers/submissions_controller.rb
@@ -1,6 +1,7 @@
 # frozen_string_literal: true
 
 class SubmissionsController < ApplicationController
+  skip_before_action :verify_authenticity_token
   before_action :load_template, only: %i[new create]
   authorize_resource :template, only: %i[new create]
 
diff --git a/app/controllers/template_documents_controller.rb b/app/controllers/template_documents_controller.rb
index abd72010..7144f5e0 100644
--- a/app/controllers/template_documents_controller.rb
+++ b/app/controllers/template_documents_controller.rb
@@ -1,6 +1,7 @@
 # frozen_string_literal: true
 
 class TemplateDocumentsController < ApplicationController
+  skip_before_action :verify_authenticity_token
   load_and_authorize_resource :template
 
   def create
diff --git a/app/models/document_generation_event.rb b/app/models/document_generation_event.rb
index 24d71ba3..f4e0a701 100644
--- a/app/models/document_generation_event.rb
+++ b/app/models/document_generation_event.rb
@@ -13,7 +13,7 @@
 # Indexes
 #
 #  index_document_generation_events_on_submitter_id                 (submitter_id)
-#  index_document_generation_events_on_submitter_id_and_event_name  (submitter_id,event_name) UNIQUE WHERE ((event_name)::text = ANY (ARRAY[('start'::character varying)::text, ('complete'::character varying)::text]))
+#  index_document_generation_events_on_submitter_id_and_event_name  (submitter_id,event_name) UNIQUE WHERE ((event_name)::text = ANY ((ARRAY['start'::character varying, 'complete'::character varying])::text[]))
 #
 # Foreign Keys
 #
diff --git a/app/models/email_event.rb b/app/models/email_event.rb
index 3aaa5db9..3c6aac61 100644
--- a/app/models/email_event.rb
+++ b/app/models/email_event.rb
@@ -20,7 +20,7 @@
 #
 #  index_email_events_on_account_id_and_event_datetime  (account_id,event_datetime)
 #  index_email_events_on_email                          (email)
-#  index_email_events_on_email_event_types              (email) WHERE ((event_type)::text = ANY (ARRAY[('bounce'::character varying)::text, ('soft_bounce'::character varying)::text, ('complaint'::character varying)::text, ('soft_complaint'::character varying)::text]))
+#  index_email_events_on_email_event_types              (email) WHERE ((event_type)::text = ANY ((ARRAY['bounce'::character varying, 'soft_bounce'::character varying, 'complaint'::character varying, 'soft_complaint'::character varying])::text[]))
 #  index_email_events_on_emailable                      (emailable_type,emailable_id)
 #  index_email_events_on_message_id                     (message_id)
 #
diff --git a/app/models/export_location.rb b/app/models/export_location.rb
index a211c0a3..0be6dfb3 100644
--- a/app/models/export_location.rb
+++ b/app/models/export_location.rb
@@ -2,15 +2,16 @@
 #
 # Table name: export_locations
 #
-#  id                  :bigint           not null, primary key
-#  api_base_url        :string           not null
-#  authorization_token :string
-#  default_location    :boolean          default(FALSE), not null
-#  extra_params        :jsonb            not null
-#  name                :string           not null
-#  templates_endpoint  :string
-#  created_at          :datetime         not null
-#  updated_at          :datetime         not null
+#  id                   :bigint           not null, primary key
+#  api_base_url         :string           not null
+#  authorization_token  :string
+#  default_location     :boolean          default(FALSE), not null
+#  extra_params         :jsonb            not null
+#  name                 :string           not null
+#  submissions_endpoint :string
+#  templates_endpoint   :string
+#  created_at           :datetime         not null
+#  updated_at           :datetime         not null
 #
 class ExportLocation < ApplicationRecord
   validates :name, presence: true
diff --git a/app/services/export_service.rb b/app/services/export_service.rb
new file mode 100644
index 00000000..10ae28d5
--- /dev/null
+++ b/app/services/export_service.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'faraday'
+
+class ExportService
+  attr_reader :error_message
+
+  def initialize
+    @error_message = nil
+  end
+
+  protected
+
+  def api_connection
+    @api_connection ||= Faraday.new(url: ExportLocation.default_location.api_base_url) do |faraday|
+      faraday.request :json
+      faraday.response :json
+      faraday.adapter Faraday.default_adapter
+    end
+  rescue StandardError => e
+    Rails.logger.error("Failed to create API connection: #{e.message}")
+    Rollbar.error(e) if defined?(Rollbar)
+    nil
+  end
+
+  def post_to_api(data, endpoint, extra_params = nil)
+    connection = api_connection
+    return nil unless connection
+
+    connection.post(endpoint) do |req|
+      data = data.merge(extra_params) if extra_params.present? && data.is_a?(Hash)
+      req.body = data.is_a?(String) ? data : data.to_json
+    end
+  end
+
+  def export_location
+    @export_location ||= ExportLocation.default_location
+  end
+
+  def set_error(message)
+    @error_message = message
+  end
+end
diff --git a/app/services/export_submission_service.rb b/app/services/export_submission_service.rb
new file mode 100644
index 00000000..23fb4398
--- /dev/null
+++ b/app/services/export_submission_service.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+class ExportSubmissionService < ExportService
+  attr_reader :submission
+
+  def initialize(submission)
+    super()
+    @submission = submission
+  end
+
+  def call
+    unless export_location&.submissions_endpoint.present?
+      set_error('Export failed: Submission export endpoint is not configured.')
+      return false
+    end
+
+    payload = build_payload
+    response = post_to_api(payload, export_location.submissions_endpoint, export_location.extra_params)
+
+    if response&.success?
+      true
+    else
+      set_error("Failed to export submission ##{submission.id} events.")
+      false
+    end
+  rescue Faraday::Error => e
+    Rails.logger.error("Failed to export submission Faraday: #{e.message}")
+    Rollbar.error("Failed to export submission: #{e.message}") if defined?(Rollbar)
+    set_error("Network error occurred during export: #{e.message}")
+    false
+  rescue StandardError => e
+    Rails.logger.error("Failed to export submission: #{e.message}")
+    Rollbar.error(e) if defined?(Rollbar)
+    set_error("An unexpected error occurred during export: #{e.message}")
+    false
+  end
+
+  private
+
+  def build_payload
+    {
+      submission_id: submission.id,
+      template_name: submission.template&.name,
+      events: submission.submission_events.order(updated_at: :desc).limit(1)
+    }
+  end
+end
diff --git a/app/services/export_template_service.rb b/app/services/export_template_service.rb
new file mode 100644
index 00000000..7f0d4e61
--- /dev/null
+++ b/app/services/export_template_service.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+class ExportTemplateService < ExportService
+  def initialize(data)
+    super()
+    @data = data
+  end
+
+  def call
+    response = post_to_api(@data, export_location.templates_endpoint, export_location.extra_params)
+
+    if response&.success?
+      Rails.logger.info("Successfully exported template #{@data[:template][:name]} to #{export_location.name}")
+      true
+    else
+      Rails.logger.error("Failed to export template to third party: #{response&.status}")
+      Rollbar.error("#{export_location.name} template export API error: #{response&.status}") if defined?(Rollbar)
+      set_error("Failed to export template to third party")
+      false
+    end
+  rescue Faraday::Error => e
+    Rails.logger.error("Failed to export template Faraday: #{e.message}")
+    Rollbar.error("Failed to export template: #{e.message}") if defined?(Rollbar)
+    set_error("Network error occurred during template export: #{e.message}")
+    false
+  rescue StandardError => e
+    Rails.logger.error("Failed to export template: #{e.message}")
+    Rollbar.error(e) if defined?(Rollbar)
+    set_error("An unexpected error occurred during template export: #{e.message}")
+    false
+  end
+end
diff --git a/app/views/templates/_submission.html.erb b/app/views/templates/_submission.html.erb
index cfa7f936..fc4804b1 100644
--- a/app/views/templates/_submission.html.erb
+++ b/app/views/templates/_submission.html.erb
@@ -101,6 +101,9 @@
             <%= t('view') %>
           
         
+        
+          <%= button_to button_title(title: nil, icon: svg_icon('upload', class: 'w-6 h-6')), export_submission_path(submission), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('export'), method: :post %>
+        
         <% if !submission.archived_at? && !template&.archived_at? && can?(:destroy, submission) %>
           
             <%= button_to button_title(title: nil, disabled_with: t(:archive).first(4), icon: svg_icon('archive', class: 'w-6 h-6')), submission_path(submission), class: 'btn btn-outline btn-sm w-full md:w-fit', form: { class: 'flex' }, title: t('archive'), method: :delete %>
diff --git a/config/routes.rb b/config/routes.rb
index 7fc306c2..18390c8a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -81,6 +81,8 @@ Rails.application.routes.draw do
   resources :submissions_archived, only: %i[index], path: 'submissions/archived'
   resources :submissions, only: %i[index], controller: 'submissions_dashboard'
   resources :submissions, only: %i[show destroy] do
+    post :export, on: :member, to: 'export#export_submission'
+
     resources :unarchive, only: %i[create], controller: 'submissions_unarchive'
     resources :events, only: %i[index], controller: 'submission_events'
   end
diff --git a/db/migrate/20250708172115_add_submissions_endpoint_to_export_locations.rb b/db/migrate/20250708172115_add_submissions_endpoint_to_export_locations.rb
new file mode 100644
index 00000000..2f3a31f8
--- /dev/null
+++ b/db/migrate/20250708172115_add_submissions_endpoint_to_export_locations.rb
@@ -0,0 +1,5 @@
+class AddSubmissionsEndpointToExportLocations < ActiveRecord::Migration[8.0]
+  def change
+    add_column :export_locations, :submissions_endpoint, :string
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 9a97cf4f..326eb62d 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[8.0].define(version: 2025_07_03_143236) do
+ActiveRecord::Schema[8.0].define(version: 2025_07_08_172115) do
   # These are extensions that must be enabled in order to support this database
   enable_extension "btree_gin"
   enable_extension "pg_catalog.plpgsql"
@@ -160,7 +160,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_07_03_143236) do
     t.string "event_name", null: false
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
-    t.index ["submitter_id", "event_name"], name: "index_document_generation_events_on_submitter_id_and_event_name", unique: true, where: "((event_name)::text = ANY (ARRAY[('start'::character varying)::text, ('complete'::character varying)::text]))"
+    t.index ["submitter_id", "event_name"], name: "index_document_generation_events_on_submitter_id_and_event_name", unique: true, where: "((event_name)::text = ANY ((ARRAY['start'::character varying, 'complete'::character varying])::text[]))"
     t.index ["submitter_id"], name: "index_document_generation_events_on_submitter_id"
   end
 
@@ -177,7 +177,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_07_03_143236) do
     t.datetime "created_at", null: false
     t.index ["account_id", "event_datetime"], name: "index_email_events_on_account_id_and_event_datetime"
     t.index ["email"], name: "index_email_events_on_email"
-    t.index ["email"], name: "index_email_events_on_email_event_types", where: "((event_type)::text = ANY (ARRAY[('bounce'::character varying)::text, ('soft_bounce'::character varying)::text, ('complaint'::character varying)::text, ('soft_complaint'::character varying)::text]))"
+    t.index ["email"], name: "index_email_events_on_email_event_types", where: "((event_type)::text = ANY ((ARRAY['bounce'::character varying, 'soft_bounce'::character varying, 'complaint'::character varying, 'soft_complaint'::character varying])::text[]))"
     t.index ["emailable_type", "emailable_id"], name: "index_email_events_on_emailable"
     t.index ["message_id"], name: "index_email_events_on_message_id"
   end
@@ -225,6 +225,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_07_03_143236) do
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
     t.jsonb "extra_params", default: {}, null: false
+    t.string "submissions_endpoint"
   end
 
   create_table "oauth_access_grants", force: :cascade do |t|
diff --git a/spec/factories/export_locations.rb b/spec/factories/export_locations.rb
new file mode 100644
index 00000000..0d23f543
--- /dev/null
+++ b/spec/factories/export_locations.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+  factory :export_location do
+    name { Faker::Company.name }
+    api_base_url { 'https://api.example.com' }
+    default_location { false }
+    extra_params { {} }
+    templates_endpoint { '/templates' }
+    submissions_endpoint { nil }
+    authorization_token { nil }
+
+    trait :with_submissions_endpoint do
+      submissions_endpoint { '/submissions' }
+    end
+
+    trait :with_authorization_token do
+      authorization_token { SecureRandom.hex(32) }
+    end
+
+    trait :default do
+      default_location { true }
+    end
+
+    trait :with_extra_params do
+      extra_params { { 'api_key' => 'test_key', 'version' => '1.0' } }
+    end
+  end
+end
diff --git a/spec/services/export_submission_service_spec.rb b/spec/services/export_submission_service_spec.rb
new file mode 100644
index 00000000..52832241
--- /dev/null
+++ b/spec/services/export_submission_service_spec.rb
@@ -0,0 +1,200 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe ExportSubmissionService do
+  let(:account) { create(:account) }
+  let(:user) { create(:user, account: account) }
+  let(:template) { create(:template, account: account, author: user) }
+  let(:submission) { create(:submission, template: template, account: account) }
+  let(:export_location) { create(:export_location, :with_submissions_endpoint) }
+  let(:service) { described_class.new(submission) }
+  let(:faraday_connection) { instance_double(Faraday::Connection) }
+  let(:faraday_response) { instance_double(Faraday::Response) }
+
+  before do
+    allow(ExportLocation).to receive(:default_location).and_return(export_location)
+    allow(Faraday).to receive(:new).and_return(faraday_connection)
+  end
+
+  describe '#call' do
+    context 'when export location is not configured' do
+      before do
+        allow(ExportLocation).to receive(:default_location).and_return(nil)
+      end
+
+      it 'returns false and sets error message' do
+        expect(service.call).to be false
+        expect(service.error_message).to eq('Export failed: Submission export endpoint is not configured.')
+      end
+    end
+
+    context 'when export location has no submissions endpoint' do
+      before do
+        allow(export_location).to receive(:submissions_endpoint).and_return(nil)
+      end
+
+      it 'returns false and sets error message' do
+        expect(service.call).to be false
+        expect(service.error_message).to eq('Export failed: Submission export endpoint is not configured.')
+      end
+    end
+
+    context 'when export location is properly configured' do
+      let(:request_double) { double('request', body: nil) }
+
+      before do
+        allow(request_double).to receive(:body=)
+        allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response)
+      end
+
+      context 'when API request succeeds' do
+        before do
+          allow(faraday_response).to receive(:success?).and_return(true)
+        end
+
+        it 'returns true' do
+          expect(service.call).to be true
+        end
+
+        it 'makes API call with correct endpoint' do
+          expect(faraday_connection).to receive(:post).with(export_location.submissions_endpoint)
+          service.call
+        end
+      end
+
+      context 'when API request fails' do
+        before do
+          allow(faraday_response).to receive(:success?).and_return(false)
+        end
+
+        it 'returns false and sets error message' do
+          expect(service.call).to be false
+          expect(service.error_message).to eq("Failed to export submission ##{submission.id} events.")
+        end
+      end
+
+      context 'when API response is nil' do
+        before do
+          allow(faraday_connection).to receive(:post).and_return(nil)
+        end
+
+        it 'returns false and sets error message' do
+          expect(service.call).to be false
+          expect(service.error_message).to eq("Failed to export submission ##{submission.id} events.")
+        end
+      end
+    end
+
+    context 'when Faraday error occurs' do
+      before do
+        allow(faraday_connection).to receive(:post).and_raise(Faraday::ConnectionFailed.new('Connection failed'))
+      end
+
+      it 'returns false and sets network error message' do
+        expect(service.call).to be false
+        expect(service.error_message).to eq('Network error occurred during export: Connection failed')
+      end
+
+      it 'logs the error' do
+        expect(Rails.logger).to receive(:error).with('Failed to export submission Faraday: Connection failed')
+        service.call
+      end
+
+      it 'reports to Rollbar if available' do
+        stub_const('Rollbar', double)
+        expect(Rollbar).to receive(:error).with('Failed to export submission: Connection failed')
+        service.call
+      end
+    end
+
+    context 'when other standard error occurs' do
+      before do
+        allow(ExportLocation).to receive(:default_location).and_raise(StandardError.new('Database error'))
+      end
+
+      it 'returns false and sets generic error message' do
+        expect(service.call).to be false
+        expect(service.error_message).to eq('An unexpected error occurred during export: Database error')
+      end
+
+      it 'logs the error' do
+        expect(Rails.logger).to receive(:error).with('Failed to export submission: Database error')
+        service.call
+      end
+
+      it 'reports to Rollbar if available' do
+        stub_const('Rollbar', double)
+        error = StandardError.new('Database error')
+        allow(ExportLocation).to receive(:default_location).and_raise(error)
+        expect(Rollbar).to receive(:error).with(error)
+        service.call
+      end
+    end
+  end
+
+  describe 'payload building' do
+    let(:request_double) { double('request', body: nil) }
+
+    before do
+      allow(request_double).to receive(:body=)
+      allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response)
+      allow(faraday_response).to receive(:success?).and_return(true)
+    end
+
+    it 'includes submission_id in payload' do
+      expect(request_double).to receive(:body=) do |body|
+        expect(JSON.parse(body)).to include('submission_id' => submission.id)
+      end
+      service.call
+    end
+
+    it 'includes template_name in payload' do
+      expect(request_double).to receive(:body=) do |body|
+        expect(JSON.parse(body)).to include('template_name' => submission.template.name)
+      end
+      service.call
+    end
+
+    it 'includes recent events in payload' do
+      expect(request_double).to receive(:body=) do |body|
+        parsed_body = JSON.parse(body)
+        expect(parsed_body).to have_key('events')
+      end
+      service.call
+    end
+
+    context 'when template is nil' do
+      before do
+        allow(submission).to receive(:template).and_return(nil)
+      end
+
+      it 'includes nil template_name in payload' do
+        expect(request_double).to receive(:body=) do |body|
+          expect(JSON.parse(body)).to include('template_name' => nil)
+        end
+        service.call
+      end
+    end
+  end
+
+  describe 'extra_params handling' do
+    let(:extra_params) { { 'api_key' => 'test_key', 'version' => '1.0' } }
+    let(:request_double) { double('request', body: nil) }
+
+    before do
+      allow(export_location).to receive(:extra_params).and_return(extra_params)
+      allow(request_double).to receive(:body=)
+      allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response)
+      allow(faraday_response).to receive(:success?).and_return(true)
+    end
+
+    it 'merges extra_params into the payload' do
+      expect(request_double).to receive(:body=) do |body|
+        parsed_body = JSON.parse(body)
+        expect(parsed_body).to include('api_key' => 'test_key', 'version' => '1.0')
+      end
+      service.call
+    end
+  end
+end
diff --git a/spec/services/export_template_service_spec.rb b/spec/services/export_template_service_spec.rb
new file mode 100644
index 00000000..cb81a74e
--- /dev/null
+++ b/spec/services/export_template_service_spec.rb
@@ -0,0 +1,158 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe ExportTemplateService do
+  let(:export_location) { create(:export_location, :default) }
+  let(:data) { { template: { name: 'Test Template' } } }
+  let(:service) { described_class.new(data) }
+  let(:faraday_connection) { instance_double(Faraday::Connection) }
+  let(:faraday_response) { instance_double(Faraday::Response) }
+
+  before do
+    allow(ExportLocation).to receive(:default_location).and_return(export_location)
+    allow(Faraday).to receive(:new).and_return(faraday_connection)
+  end
+
+  describe '#call' do
+    let(:request_double) { double('request', body: nil) }
+
+    before do
+      allow(request_double).to receive(:body=)
+      allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response)
+    end
+
+    context 'when API request succeeds' do
+      before do
+        allow(faraday_response).to receive(:success?).and_return(true)
+      end
+
+      it 'returns true' do
+        expect(service.call).to be true
+      end
+
+      it 'makes API call with correct endpoint' do
+        expect(faraday_connection).to receive(:post).with(export_location.templates_endpoint)
+        service.call
+      end
+
+      it 'logs success message' do
+        expect(Rails.logger).to receive(:info).with("Successfully exported template Test Template to #{export_location.name}")
+        service.call
+      end
+    end
+
+    context 'when API request fails' do
+      before do
+        allow(faraday_response).to receive(:success?).and_return(false)
+        allow(faraday_response).to receive(:status).and_return(422)
+      end
+
+      it 'returns false and sets error message' do
+        expect(service.call).to be false
+        expect(service.error_message).to eq('Failed to export template to third party')
+      end
+
+      it 'logs error message' do
+        expect(Rails.logger).to receive(:error).with('Failed to export template to third party: 422')
+        service.call
+      end
+
+      it 'reports to Rollbar if available' do
+        stub_const('Rollbar', double)
+        expect(Rollbar).to receive(:error).with("#{export_location.name} template export API error: 422")
+        service.call
+      end
+    end
+
+    context 'when API response is nil' do
+      before do
+        allow(faraday_connection).to receive(:post).and_return(nil)
+      end
+
+      it 'returns false and sets error message' do
+        expect(service.call).to be false
+        expect(service.error_message).to eq('Failed to export template to third party')
+      end
+    end
+
+    context 'when Faraday error occurs' do
+      before do
+        allow(faraday_connection).to receive(:post).and_raise(Faraday::ConnectionFailed.new('Connection failed'))
+      end
+
+      it 'returns false and sets network error message' do
+        expect(service.call).to be false
+        expect(service.error_message).to eq('Network error occurred during template export: Connection failed')
+      end
+
+      it 'logs the error' do
+        expect(Rails.logger).to receive(:error).with('Failed to export template Faraday: Connection failed')
+        service.call
+      end
+
+      it 'reports to Rollbar if available' do
+        stub_const('Rollbar', double)
+        expect(Rollbar).to receive(:error).with('Failed to export template: Connection failed')
+        service.call
+      end
+    end
+
+    context 'when other standard error occurs' do
+      before do
+        allow(ExportLocation).to receive(:default_location).and_raise(StandardError.new('Database error'))
+      end
+
+      it 'returns false and sets generic error message' do
+        expect(service.call).to be false
+        expect(service.error_message).to eq('An unexpected error occurred during template export: Database error')
+      end
+
+      it 'logs the error' do
+        expect(Rails.logger).to receive(:error).with('Failed to export template: Database error')
+        service.call
+      end
+
+      it 'reports to Rollbar if available' do
+        stub_const('Rollbar', double)
+        error = StandardError.new('Database error')
+        allow(ExportLocation).to receive(:default_location).and_raise(error)
+        expect(Rollbar).to receive(:error).with(error)
+        service.call
+      end
+    end
+  end
+
+  describe 'data handling' do
+    let(:request_double) { double('request', body: nil) }
+
+    before do
+      allow(request_double).to receive(:body=)
+      allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response)
+      allow(faraday_response).to receive(:success?).and_return(true)
+    end
+
+    it 'sends the data in the request body' do
+      expect(request_double).to receive(:body=) do |body|
+        expect(JSON.parse(body)).to eq(data.deep_stringify_keys)
+      end
+      service.call
+    end
+
+    context 'when extra_params are provided' do
+      let(:extra_params) { { 'api_key' => 'test_key', 'version' => '1.0' } }
+
+      before do
+        allow(export_location).to receive(:extra_params).and_return(extra_params)
+      end
+
+      it 'merges extra_params into the data' do
+        expect(request_double).to receive(:body=) do |body|
+          parsed_body = JSON.parse(body)
+          expect(parsed_body).to include('api_key' => 'test_key', 'version' => '1.0')
+        end
+        service.call
+      end
+    end
+  end
+end