Campaigns drive bulk outbound calling. You manage campaigns and their contacts with Guava.Campaigns, then serve calls with an agent via Guava.attach_campaign/3.

Managing campaigns

client = Guava.Client.new!()

{:ok, campaigns} = Guava.Campaigns.list(client)          # => [%Guava.Campaign{}, ...]
{:ok, campaign} = Guava.Campaigns.get_by_code(client, "camp_abc")

{:ok, counts} = Guava.Campaigns.status(client, campaign)
# => %{"completed" => 12, "trying" => 3}

{:ok, true} = Guava.Campaigns.has_callable_contacts(client, campaign)
{:ok, :ok} = Guava.Campaigns.delete(client, campaign)

Campaigns are identified by their code — the same code you pass to Guava.attach_campaign/3. Every function takes either a code or a %Guava.Campaign{}, so you can skip the fetch when you already know the code:

{:ok, counts} = Guava.Campaigns.status(client, "camp_abc")

A Guava.Campaign has :code and :name. (It also carries a legacy :id that a few endpoints are still keyed on. That's an internal detail — no function accepts one, and passing a fetched struct just saves the SDK a lookup.)

Uploading contacts

contacts = [
  Guava.Contact.new("+14155550100", data: %{"name" => "Ada", "order_id" => 4471}),
  Guava.Contact.new("+14155550111", data: %{"name" => "Alan"})
]

{:ok, created} = Guava.Campaigns.upload_contacts(client, "camp_abc", contacts,
  allow_duplicates: false,
  accepted_terms_of_service: true
)
# => {:ok, 2} — the number of contacts inserted

A %Guava.Campaign{} works here too, if you already have one.

Each contact's :data map is delivered to your agent as initial call variables when that contact is dialed — read them with Guava.Call.get_variable/3.

Options:

  • :allow_duplicates (default false)
  • :accepted_terms_of_service (default false) — you must have the right to contact these numbers.
  • :outreach_modalities — applied to contacts that don't set their own (currently ["sms"]).

Serving campaign calls

defmodule SurveyAgent do
  use Guava.Agent, name: "Nova", organization: "Acme"

  @impl true
  def handle_start(call, state) do
    Guava.Call.reach_person(call, Guava.Call.get_variable(call, "name", "there"))
    {:noreply, state}
  end

  @impl true
  def handle_task_complete("reach_person", call, state) do
    if Guava.Call.get_field(call, "contact_availability") == "available" do
      run_survey(call)
    else
      Guava.Call.hangup(call)
    end

    {:noreply, state}
  end
end

# Supervised, or blocking for a script:
{Guava.Channel, agent: SurveyAgent, campaign: "camp_abc"}
Guava.attach_campaign(SurveyAgent, "camp_abc")

The channel connects to the campaign and, for each contact the server dispatches, runs your agent against that call.

Next: Messaging.