Skip to main content

Outbound Campaigns

Drip a prospect campaign through a sender pool with mailbox ramping, daily caps, threaded follow-ups, and auto-pause on reply.

Sender pool

Health, capacity, and per-mailbox spacing update as outcomes run.

Enroll prospects to see the outbound outcome grid.

Sequence steps

Follow each step as it queues up, sends, and completes.

Sequence code
tenant_key = "demo"

sender_attrs = [
  {"Goodway Alex", "alex@goodway.dev"},
  {"Goodway Jamie", "jamie@goodway.dev"},
  {"Goodway Sam", "sam@goodway.dev"}
]

senders =
  Enum.map(sender_attrs, fn {name, email} ->
    {:ok, adapter} =
      DripDrop.create_channel_adapter(%{
        tenant_key: tenant_key,
        name: name,
        channel: "email",
        provider: "local",
        is_default: false,
        credentials: %{"from" => "#{name} <#{email}>"},
        daily_cap: 45,
        min_gap_seconds: 2
      })

    adapter
  end)

{:ok, pool} =
  DripDrop.create_adapter_pool(%{
    tenant_key: tenant_key,
    name: "outbound_pool",
    on_pin_unavailable: :reassign
  })

Enum.each(senders, fn adapter ->
  {:ok, _member} =
    DripDrop.add_pool_member(pool, %{
      tenant_key: tenant_key,
      adapter_id: adapter.id,
      class: "mailbox",
      weight: 1,
      active: true
    })
end)

{:ok, sequence} =
  DripDrop.create_sequence(%{
    tenant_key: tenant_key,
    name: "Goodway Elixir consulting outbound",
    key: "outbound-campaigns",
    description: "Outbound campaign for Goodway Elixir software development consulting.",
    active: true,
    metadata: %{"demo" => true}
  })

{:ok, version} =
  DripDrop.create_sequence_version(sequence.id, %{
    version: 1,
    name: "Goodway consulting campaign",
    mode: :outbound,
    config: %{"pool_id" => pool.id}
  })

{:ok, intro} =
  DripDrop.create_step(version.id, %{
    name: "Initial consulting email",
    key: "consulting-intro",
    position: 1,
    channel: "email",
    timing: %{type: "immediate"},
    template_type: "inline",
    template_content: %{
      "subject" => "Phoenix and LiveView help for {{ company }}",
      "text" =>
        "Hi {{ first_name }}, Goodway helps teams stabilize Phoenix apps, clean up LiveView bottlenecks, and ship Elixir features without slowing the product team down. Is that relevant for {{ company }} this quarter?",
      "html" =>
        "<p>Hi {{ first_name }}, Goodway helps teams stabilize Phoenix apps, clean up LiveView bottlenecks, and ship Elixir features without slowing the product team down. Is that relevant for {{ company }} this quarter?</p>"
    },
    config: %{"quiet_hours" => false, "spintax_seed" => "goodway-elixir-intro"}
  })

{:ok, follow_up} =
  DripDrop.create_step(version.id, %{
    name: "Threaded follow-up",
    key: "consulting-follow-up",
    position: 2,
    channel: "email",
    timing: %{type: "delay", delay_amount: 10, delay_unit: "seconds"},
    template_type: "inline",
    template_content: %{
      "subject" => "Re: Phoenix and LiveView help for {{ company }}",
      "text" =>
        "A lot of Phoenix teams call us when LiveView screens get state-heavy, background jobs hide production issues, or releases need someone senior to pair with the internal team. Worth comparing notes?",
      "html" =>
        "<p>A lot of Phoenix teams call us when LiveView screens get state-heavy, background jobs hide production issues, or releases need someone senior to pair with the internal team. Worth comparing notes?</p>"
    },
    config: %{"quiet_hours" => false, "reply_behavior" => "pause_enrollment"}
  })

{:ok, final_bump} =
  DripDrop.create_step(version.id, %{
    name: "Final bump",
    key: "consulting-final-bump",
    position: 3,
    channel: "email",
    timing: %{type: "delay", delay_amount: 12, delay_unit: "seconds"},
    template_type: "inline",
    template_content: %{
      "subject" => "Re: Phoenix and LiveView help for {{ company }}",
      "text" =>
        "Last note from me. If Phoenix, LiveView, or Elixir reliability work sits with someone else at {{ company }}, who is the better person to ask?",
      "html" =>
        "<p>Last note from me. If Phoenix, LiveView, or Elixir reliability work sits with someone else at {{ company }}, who is the better person to ask?</p>"
    },
    config: %{"quiet_hours" => false, "reply_behavior" => "pause_enrollment"}
  })

for {from_step, to_step} <- [
      {intro, follow_up},
      {follow_up, final_bump},
      {final_bump, nil}
    ] do
  {:ok, _transition} =
    DripDrop.create_step_transition(version.id, %{
      from_step_id: from_step.id,
      to_step_id: to_step && to_step.id,
      condition_mode: "always",
      priority: 1
    })
end

{:ok, _validated} = DripDrop.validate_sequence_version(version.id)
{:ok, _activated} = DripDrop.activate_sequence_version(version.id)

for {first_name, company, email} <- [
      {"Mia", "Northstar SaaS", "mia@northstar.example"},
      {"Jordan", "Atlas Metrics", "jordan@atlas.example"},
      {"Priya", "SignalOps", "priya@signalops.example"},
      {"Eli", "Runway Data", "eli@runway.example"},
      {"Nora", "StackPilot", "nora@stackpilot.example"},
      {"Theo", "OpsCanvas", "theo@opscanvas.example"},
      {"Avery", "MetricForge", "avery@metricforge.example"},
      {"Quinn", "LaunchGrid", "quinn@launchgrid.example"}
    ] do
  DripDrop.enroll(%{
    sequence_id: sequence.id,
    subscriber_type: "prospect",
    subscriber_id: "goodway-prospect-#{System.unique_integer([:positive])}",
    tenant_key: tenant_key,
    data: %{
      "first_name" => first_name,
      "company" => company,
      "email" => email,
      "interest" => "Elixir software development consulting"
    }
  })
end

Sequence messages

Outbound emails appear here as each campaign step is sent.

Sequence logs
# Live DripDrop events will stream here after dispatch starts.
A project by Goodway — we build software that drives results.