Integration walkthrough

A step-by-step guide to integrating EmailList into your application. This walkthrough covers everything from installation to sending emails with unsubscribe links.

What you'll learn

  • How to install and configure the EmailList API client
  • How to create projects and lists (manually or programmatically)
  • How to import existing users using bulk operations (optional)
  • How to sync your user model with EmailList contacts
  • How to fetch contacts before sending emails
  • How to include unsubscribe links in your email templates

Step 1: Installation

First, install and configure the EmailList API client. For detailed installation instructions, see the installation guide.

Quick start:

# Add to Gemfile
gem 'email_list_api'

# Configure in config/initializers/email_list_api.rb
EmailListApi.configure do |config|
  config.api_key = ENV['EMAILLIST_API_KEY']
end

# Create client
client = EmailListApi::Client.new

Important: Store your API key in environment variables. Never commit API keys to version control. Get your API key from the dashboard → Settings → API keys.

Step 2: Create projects and lists

Projects are top-level containers for organizing your email lists. Lists are collections of contacts within a project. You can create them manually through the dashboard or programmatically via the API.

Option A: Create manually

Use the dashboard to create projects and lists:

  1. Sign in to your EmailList dashboard
  2. Navigate to Projects → Create new project
  3. Within your project, create lists as needed

Option B: Create programmatically

Create projects and lists programmatically when your app starts or deploys. Use the upsert endpoints to ensure they exist without errors if they're already created:

# Create or get existing project (fails gracefully if already exists)
def ensure_project_exists(client, project_name)
  begin
    response = client.upsert_project(name: project_name)
    project_id = response[:data][:id]
    Rails.logger.info "Project '#{project_name}' ready (ID: #{project_id})"
    project_id
  rescue => e
    Rails.logger.error "Failed to create project: #{e.message}"
    # Optionally, try to find existing project
    projects = client.projects
    existing = projects[:data].find { |p| p[:name] == project_name }
    existing&.dig(:id) || raise
  end
end

# Create or get existing list (fails gracefully if already exists)
def ensure_list_exists(client, project_id, list_name)
  begin
    response = client.upsert_list(
      project_id: project_id,
      name: list_name
    )
    list_id = response[:data][:id]
    Rails.logger.info "List '#{list_name}' ready (ID: #{list_id})"
    list_id
  rescue => e
    Rails.logger.error "Failed to create list: #{e.message}"
    # Optionally, try to find existing list
    lists = client.lists(project_id: project_id)
    existing = lists[:data].find { |l| l[:name] == list_name }
    existing&.dig(:id) || raise
  end
end

# Use in an initializer or startup script
Rails.application.config.after_initialize do
  client = EmailListApi::Client.new
  
  project_id = ensure_project_exists(client, 'My App')
  ensure_list_exists(client, project_id, 'Newsletter')
  ensure_list_exists(client, project_id, 'Product Updates')
end

Tip: The upsert endpoints return 201 Created for new resources and 200 OK for existing ones, making them perfect for initialization scripts that run on every deploy.

Step 3: Import existing users (optional)

If you have existing users in your application, you can import them all at once using the bulk operations endpoint. This is a one-time operation to get your existing user base into EmailList.

Note

This step is optional and only needed if you have existing users to import. For new users going forward, use Step 4 to sync them automatically.

# Import all existing users using bulk operations
client = EmailListApi::Client.new
project_id = ENV['EMAILLIST_PROJECT_ID'].to_i

# Fetch all users from your database
users = User.all

# Prepare contacts array (process in batches for large datasets)
contacts = users.map do |user|
  {
    email: user.email,
    first_name: user.first_name,
    last_name: user.last_name
  }
end

# Import in batches (API processes up to 10,000 at a time)
contacts.each_slice(1000) do |batch|
  begin
    response = client.bulk_create_contacts(
      project_id: project_id,
      contacts: batch
    )
    
    created = response[:data][:created]
    failed = response[:data][:failed]
    
    Rails.logger.info "Imported #{created} contacts, #{failed} failed"
    
    # Log any errors
    if response[:data][:errors].any?
      Rails.logger.warn "Errors: #{response[:data][:errors].inspect}"
    end
  rescue => e
    Rails.logger.error "Failed to import batch: #{e.message}"
    # Continue with next batch even if one fails
  end
end

Tips: The bulk endpoint processes contacts in batches of 10,000. Duplicate emails and existing contacts are automatically skipped. For large imports, process in smaller batches (1,000-5,000) to avoid timeouts. See the bulk operations documentation for more details.

Step 4: Integrate with your user model

Sync your application's users with EmailList contacts. When a user signs up or updates their information, create or update their contact in EmailList.

Using the upsert endpoint

The upsert endpoint creates a contact if it doesn't exist, or updates it if it does. This is perfect for syncing users:

# In your User model or service
class User < ApplicationRecord
  after_create :sync_to_email_list
  after_update :sync_to_email_list, if: :saved_change_to_email?
  
  private
  
  def sync_to_email_list
    return unless email.present?
    
    client = EmailListApi::Client.new
    
    begin
      # Upsert contact - creates if new, updates if exists
      client.upsert_contact(
        project_id: ENV['EMAILLIST_PROJECT_ID'].to_i,
        email: email,
        first_name: first_name,
        last_name: last_name
      )
      
      # Optionally add to a list
      if newsletter_subscribed?
        client.add_contacts_to_list(
          project_id: ENV['EMAILLIST_PROJECT_ID'].to_i,
          list_id: ENV['EMAILLIST_NEWSLETTER_LIST_ID'].to_i,
          contacts: [{ email: email }]
        )
      end
    rescue => e
      # Fail gracefully - log error but don't break user signup
      Rails.logger.error "Failed to sync user to EmailList: #{e.message}"
      # Optionally use an error tracking service like Sentry
      # Sentry.capture_exception(e)
    end
  end
end

Rails integration (automatic syncing)

If you're using Ruby on Rails, the EmailList API gem includes a concern that automatically syncs your models:

class User < ApplicationRecord
  include EmailListApi::SyncsEmailListContact
  
  # Automatically syncs when user is created or updated
  syncs_email_list_contact project_slug: 'my-app'
end

Learn more about the Rails integration →

Important: Fail gracefully

Always wrap EmailList API calls in error handling. If the API is temporarily unavailable, your user signup/update process should still succeed. Log errors for monitoring, but don't let EmailList failures break your core application flow.

Step 5: Get contacts before sending emails

Before sending emails, fetch the contacts from your list. The API response includes unsubscribe URLs that you'll need for each contact.

# Fetch all contacts from a list (with pagination)
def fetch_all_contacts(client, project_id, list_id)
  contacts = []
  page = 1
  
  loop do
    response = client.list_contacts(
      project_id: project_id,
      list_id: list_id,
      page: page
    )
    
    page_contacts = response[:data]
    break if page_contacts.empty?
    
    contacts.concat(page_contacts)
    
    # Check if there are more pages
    meta = response[:meta]
    break if page >= meta[:total_pages]
    
    page += 1
  end
  
  contacts
end

# Use before sending emails
client = EmailListApi::Client.new
contacts = fetch_all_contacts(
  client,
  ENV['EMAILLIST_PROJECT_ID'].to_i,
  ENV['EMAILLIST_NEWSLETTER_LIST_ID'].to_i
)

contacts.each do |contact|
  email = contact[:email]
  unsubscribe_url = contact[:unsubscribe_url]
  
  # Send email with unsubscribe link
  send_email(email, unsubscribe_url)
end

Note: Always fetch contacts right before sending emails to ensure you have the latest unsubscribe URLs. The API paginates results (25 per page by default), so make sure to handle pagination if you have more contacts.

Step 6: Include unsubscribe links in email templates

Each contact response includes an unsubscribe_url that you must include in your email templates. This URL is unique to each contact-list combination and allows recipients to unsubscribe without authentication.

Here's an example showing how to include the unsubscribe URL in your email templates:

Note: In your ERB email template, use <%= unsubscribe_url %> to output the unsubscribe URL. In JavaScript template strings, use ${unsubscribeUrl}.

# When sending emails to contacts from a list
contacts.each do |contact|
  unsubscribe_url = contact[:unsubscribe_url]
  
  # Render email template with unsubscribe URL
  # In your ERB template, use: <a href="UNSUBSCRIBE_URL">Unsubscribe</a>
  # Replace UNSUBSCRIBE_URL with the unsubscribe_url variable
  email_body = render_email_template(
    contact: contact,
    unsubscribe_url: unsubscribe_url
  )
  
  # Send email using your email service (e.g., ActionMailer, SendGrid, etc.)
  send_email(
    to: contact[:email],
    subject: 'Your weekly newsletter',
    body: email_body
  )
end

Best practices

  • Always include unsubscribe links in every marketing email (required by CAN-SPAM, GDPR, etc.)
  • Place the unsubscribe link in the footer where recipients can easily find it
  • Use clear, user-friendly text like "Unsubscribe" or "Manage preferences"
  • Fetch unsubscribe URLs right before sending to ensure they're current and valid
  • Each unsubscribe URL is specific to a contact-list combination

For more details on unsubscribe URLs, see the unsubscribe URLs documentation.

Complete example

Here's a complete example that ties everything together:

# Complete integration example
class NewsletterService
  def initialize
    @client = EmailListApi::Client.new
    @project_id = ENV['EMAILLIST_PROJECT_ID'].to_i
    @list_id = ENV['EMAILLIST_NEWSLETTER_LIST_ID'].to_i
  end
  
  # Send newsletter to all subscribers
  def send_newsletter
    contacts = fetch_all_contacts
    
    contacts.each do |contact|
      next unless contact[:unsubscribe_url] # Skip if no unsubscribe URL
      
      begin
        send_email_with_unsubscribe(
          to: contact[:email],
          unsubscribe_url: contact[:unsubscribe_url]
        )
      rescue => e
        Rails.logger.error "Failed to send email to #{contact[:email]}: #{e.message}"
        # Continue with other contacts even if one fails
      end
    end
  end
  
  private
  
  def fetch_all_contacts
    contacts = []
    page = 1
    
    loop do
      response = @client.list_contacts(
        project_id: @project_id,
        list_id: @list_id,
        page: page
      )
      
      page_contacts = response[:data]
      break if page_contacts.empty?
      
      contacts.concat(page_contacts)
      
      meta = response[:meta]
      break if page >= meta[:total_pages]
      
      page += 1
    end
    
    contacts
  end
  
  def send_email_with_unsubscribe(to:, unsubscribe_url:)
    # Render your email template with the unsubscribe URL
    email_body = render_email_template(unsubscribe_url: unsubscribe_url)
    
    # Send using your email service (ActionMailer, SendGrid, etc.)
    NewsletterMailer.weekly_update(
      to: to,
      body: email_body
    ).deliver_now
  end
  
  def render_email_template(unsubscribe_url:)
    # Your email template rendering logic
    # Include the unsubscribe_url in the template
  end
end

Next steps