Skip to content

Scheduling Example

This is a simple outbound appointment scheduling bot for a dental office. To run the example, first save the example below to a new file called scheduling-outbound.ts, then run the following commands:

# Export your personal Guava API key and Agent phone number
$ export GUAVA_API_KEY="gva-XXX"
$ export GUAVA_AGENT_NUMBER="+1XXXXXXXXXX"
$ npm install @guava-ai/guava-sdk
$ npx tsx ./scheduling-outbound.ts +15555555555 'your name'

Be sure to replace the phone number and name in the last line above with your own.

scheduling-outbound.ts
import * as guava from "@guava-ai/guava-sdk";
import {DatetimeFilter} from "@guava-ai/guava-sdk/helpers/openai";
import { mockAppointmentsForFuture } from "@guava-ai/guava-sdk/example-data";

class SchedulingController extends guava.CallController {
  private readonly patientName: string;
  private readonly datetimeFilter: DatetimeFilter;

  constructor(patientName: string) {
    super();

    this.patientName = patientName;

    // This is a basic appointment time selector that pulls from a mock list of appointments.
    // In production, you would likely replace this with your own agentic scheduling backend.
    this.datetimeFilter = new DatetimeFilter({
      sourceList: mockAppointmentsForFuture(),
    });

    // Use setPersona to set basic information about the agent, as well as its high-level purpose.
    this.setPersona({
      organizationName: "Bright Smile Dental",
      agentName: "Grace",
      agentPurpose: `You are calling ${patientName} to help them schedule a dental appointment`,
    });

    // reachPerson is a convenience function to confirm that we are talking to the intended recipient.
    this.reachPerson(this.patientName, {
      onSuccess: () => this.scheduleRecipient(),
      onFailure: () => this.recipientUnavailable(),
    });
  }

  private scheduleRecipient() {
    // We have now confirmed that we are talking to the patient.
    // Set the Agent's current task to collect the desired appointment time.
    this.setTask(
      {
        // The check list is an ordered list of items for the agent to go through.
        // It can include short prompts as well as Fields to capture structured information.
        checklist: [
          "tell them that it's been a while since their regular cleaning with Dr. Teeth, and ask if they would like to schedule an appointment now.",
          // We have one field to collect, which is the appointment_time.
          guava.Field({
            key: "appointment_time",
            fieldType: "calendar_slot",
            description: "Find a time that works for the caller",

            // The choiceGenerator will be called when negotiating a calendar slot.
            // We respond with a list of datetime options based on the user's
            // criteria, which is summarized in the query string.
            choiceGenerator: async (query: string) => {
              // Query will have some natural language preferences like "early morning" or
              // "next week". The dateTime filter queries our calendar for matching slots.
              const result = await this.datetimeFilter.filter(query, { maxResults: 3 });
              this.logger.info("Appointment slot matches: %s", JSON.stringify(result));
              return result;
            },
          }),
          'tell them their appointment has been confirmed and answer any questions before ending the call.',
        ],
      },
      () => this.hangup("Thank them for their time and hang up the call."),
    );
  }

  private recipientUnavailable() {
    this.hangup("Apologize for your mistake and hang up the call.");
  }

  override onSessionDone() {
    // This callback is invoked at the end of the bot session.
    const selectedTime = this.getField("appointment_time");
    if (selectedTime) {
      this.logger.info(`Appointment confirmed for: ${selectedTime}`);
    }
  }
}

export function run(args: string[]) {
  const [toNumber, patientName = "Benjamin Buttons"] = args;

  if (!toNumber) {
    console.error("Usage: guava-example scheduling-outbound <phone> [name]");
    process.exit(1);
  }

  new guava.Client().createOutbound(
    process.env.GUAVA_AGENT_NUMBER,
    toNumber,
    new SchedulingController(patientName),
  );
}

run(process.argv.slice(2));