> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goguava.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Outbound Campaigns

> Call many contacts with automatic retries, calling windows, and concurrency control

While you can use `agent.call_phone("+1...")` / `agent.callPhone("+1...")` to place singular outbound calls, we recommend using Campaigns when calling multiple contacts.

Campaigns automatically manage missed-call retries, enforce calling windows, and control call concurrency.
Since they are persistent resources, you should create one Campaign for each distinct use case and continue adding contacts to it as needed.

While Campaigns are responsible for dispatching calls - you will still need to "attach" an Agent to handle the call itself - this is done using a unique "code" created for each Campaign.

<Note>
  Prefer to manage campaigns programmatically over HTTP? See the [Campaigns API](/api-reference/campaigns-api) reference for the full set of REST endpoints — create, upload contacts, status, and more.
</Note>

## Before you start

You'll need:

* **A Guava account** — sign up at [app.goguava.ai](https://app.goguava.ai)
* **A phone number** — from the [Phone Numbers](https://app.goguava.ai/dashboard/phone-numbers) page
* **Outbound permission** — outbound dialing requires approval. See [Outbound & SMS Compliance](/guides/outbound-and-sms-permissions) for the required registrations, then fill out the **Outbound Dialing Permissions Request** form on the [Compliance](https://app.goguava.ai/dashboard/compliance) page.

## Create a campaign in the dashboard

Open the [Campaigns](https://app.goguava.ai/dashboard/campaigns) page and click **Create Campaign**. Configure your campaign name, origin phone numbers, calling windows, and retry settings.

Once created, take note of the campaign code — it starts with `gcmp-...`.

## Upload contacts

From the campaign's detail page in the dashboard, upload your contact list. Each contact needs a phone number and any per-call data variables your agent will use (such as a patient name or appointment time).

These variables will be accessible inside your agent callbacks via `call.get_variable()` / `call.getVariable()`.

## Write your agent

Define an `Agent` and attach callbacks for each stage of the call.

<CodeGroup>
  ```python Python theme={null}
  import guava
  from guava import Agent, Field

  agent = Agent(
      name="Sarah",
      organization="Valley Dental",
      purpose="Remind patients about their upcoming dental appointments.",
  )

  # Fires at the start of every call. We'll start by using reach_person()
  # to confirm we're talking to the right person.
  @agent.on_call_start
  def on_call_start(call: guava.Call):
      call.reach_person(
          contact_full_name=call.get_variable("patient_name"),
      )

  # Fires once reach_person() resolves. outcome="available" means the contact answered.
  @agent.on_reach_person
  def on_reach_person(call: guava.Call, outcome: str):
      if outcome == "available":
          # Appointment details are only loaded into the context after the
          # caller's identity has been confirmed.
          call.add_info("appointment_details", {
              "patient_name": call.get_variable("patient_name"),
              "appointment_date": call.get_variable("appointment_date"),
              "appointment_time": call.get_variable("appointment_time")
          })

          call.set_task(
              "confirm_appointment",
              objective="Confirm the appointment date and time.",
              checklist=[
                  Field(
                      key="appointment_response",
                      description="Whether the patient confirms, reschedules, or cancels",
                      field_type="multiple_choice",
                      choices=["confirmed", "reschedule", "cancel"],
                  ),
              ],
          )
      else:
          call.hangup()

  # Fires when all checklist items are resolved.
  @agent.on_task_complete("confirm_appointment")
  def on_confirmed(call: guava.Call):
      call.hangup("Thank them and end the call.")
  ```

  ```typescript TypeScript theme={null}
  import * as guava from "@guava-ai/guava-sdk";

  const agent = new guava.Agent({
    name: "Sarah",
    organization: "Valley Dental",
    purpose: "Remind patients about their upcoming dental appointments.",
  });

  // Fires at the start of every call. We'll start by using reachPerson()
  // to confirm we're talking to the right person.
  agent.onCallStart(async (call: guava.Call) => {
    await call.reachPerson(await call.getVariable("patient_name"));
  });

  // Fires once reachPerson() resolves. outcome="available" means the contact answered.
  agent.onReachPerson(async (call: guava.Call, outcome: string) => {
    if (outcome === "available") {
      // Appointment details are only loaded into the context after the
      // caller's identity has been confirmed.
      await call.addInfo("appointment_details", {
        patient_name: await call.getVariable("patient_name"),
        appointment_date: await call.getVariable("appointment_date"),
        appointment_time: await call.getVariable("appointment_time"),
      });

      await call.setTask({
        taskId: "confirm_appointment",
        objective: "Confirm the appointment date and time.",
        checklist: [
          guava.Field({
            key: "appointment_response",
            description: "Whether the patient confirms, reschedules, or cancels",
            fieldType: "multiple_choice",
            choices: ["confirmed", "reschedule", "cancel"],
          }),
        ],
      });
    } else {
      await call.hangup();
    }
  });

  // Fires when all checklist items are resolved.
  agent.onTaskComplete("confirm_appointment", async (call) => {
    await call.hangup("Thank them and end the call.");
  });
  ```
</CodeGroup>

## Test your agent

<Tabs>
  <Tab title="Python">
    Before attaching to a campaign, test your agent using `agent.call_local()` or `agent.chat()`. Pass a `variables` dict to simulate per-contact data:

    ```python campaign.py theme={null}
    agent.chat(variables={
        "patient_name": "Jane Smith",
        "appointment_date": "Monday, June 23rd",
        "appointment_time": "2:00 PM",
    })
    ```
  </Tab>

  <Tab title="TypeScript">
    Before attaching to a campaign, test your agent using `agent.callLocal()` or `agent.chat()`. Pass a `variables` object to simulate per-contact data:

    ```typescript campaign.ts theme={null}
    await agent.chat({
      patient_name: "Jane Smith",
      appointment_date: "Monday, June 23rd",
      appointment_time: "2:00 PM",
    });
    ```
  </Tab>
</Tabs>

## Attach and serve the campaign

<Tabs>
  <Tab title="Python">
    Next, run `agent.attach_campaign("gcmp-...")`. This "attaches" your agent to the Campaign that we previously created.

    ```python campaign.py theme={null}
    agent.attach_campaign(campaign_code="gcmp-...")
    ```
  </Tab>

  <Tab title="TypeScript">
    Next, call `await agent.attachCampaign("gcmp-...")`. This "attaches" your agent to the Campaign that we previously created.

    ```typescript campaign.ts theme={null}
    await agent.attachCampaign("gcmp-...");
    ```
  </Tab>
</Tabs>

## Review calls in the dashboard

Once your campaign is running, visit the [Campaigns](https://app.goguava.ai/dashboard/campaigns) page to monitor progress.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.