> ## 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.

# Inbound Form Filling

> Build an inbound agent that collects information from callers

In this example, we'll build an inbound voice agent for a fictional restaurant. Callers can join the waitlist by providing their name, party size, and a callback number — which the agent collects conversationally using a structured task.

## Define the Agent

`guava.Agent` is our starting point for building Guava agents. We'll create one with a name and purpose scoped to the restaurant.

<CodeGroup>
  ```python Python theme={null}
  import guava

  agent = guava.Agent(
      name="Mia",
      organization="Thai Palace",
      purpose="Helping callers join the restaurant waitlist",
  )
  ```

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

  const agent = new guava.Agent({
    name: "Mia",
    organization: "Thai Palace",
    purpose: "Helping callers join the restaurant waitlist",
  });
  ```
</CodeGroup>

## Accept or reject the call

`on_call_received` fires before the call starts and gives you a chance to accept or reject based on caller info. Here we accept all calls.

<CodeGroup>
  ```python Python theme={null}
  @agent.on_call_received
  def on_call_received(call_info: guava.CallInfo) -> guava.IncomingCallAction:
      return guava.AcceptCall()
  ```

  ```typescript TypeScript theme={null}
  agent.onCallReceived(async (_callInfo: guava.CallInfo) => {
    return { action: "accept" };
  });
  ```
</CodeGroup>

<Note>
  If you don't register `on_call_received`, Guava accepts all calls by default. Implement it only when you need to screen callers or look up information based off the incoming phone number.
</Note>

## Set up the form

`on_call_start` fires at the beginning of every accepted call. We use `set_task` to hand the agent a structured checklist of fields to collect. The agent gathers each piece of information conversationally — it knows when all fields are filled and automatically moves on.

<CodeGroup>
  ```python Python theme={null}
  @agent.on_call_start
  def on_call_start(call: guava.Call) -> None:
      call.set_task(
          "waitlist",
          objective="You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
          checklist=[
              guava.Field(key="caller_name", field_type="text", description="Name for the waitlist"),
              guava.Field(key="party_size", field_type="integer", description="Number of people"),
              guava.Field(
                  key="phone_number",
                  field_type="text",
                  description="Phone number to text when the table is ready",
              ),
              "Read the phone number back to the caller to confirm.",
          ],
      )
  ```

  ```typescript TypeScript theme={null}
  agent.onCallStart(async (call: guava.Call) => {
    await call.setTask({
      taskId: "waitlist",
      objective: "You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
      checklist: [
        guava.Field({ key: "caller_name", fieldType: "text", description: "Name for the waitlist" }),
        guava.Field({ key: "party_size", fieldType: "integer", description: "Number of people" }),
        guava.Field({
          key: "phone_number",
          fieldType: "text",
          description: "Phone number to text when the table is ready",
        }),
        "Read the phone number back to the caller to confirm.",
      ],
    });
  });
  ```
</CodeGroup>

<Note>
  The checklist can mix `Field` objects (typed, named values the agent extracts) with plain strings (freeform instructions the agent follows). Fields are retrievable later via `get_field()`.
</Note>

## Handle task completion

`on_task_complete` fires once every field in the checklist is collected. This is the right place to save the data to your backend, trigger a notification, or hang up.

<CodeGroup>
  ```python Python theme={null}
  @agent.on_task_complete("waitlist")
  def on_waitlist_done(call: guava.Call) -> None:
      logger.info(
          "Added %s, party of %d, to waitlist.",
          call.get_field("caller_name"),
          call.get_field("party_size"),
      )
      call.hangup("Thank the caller and let them know we'll text when their table is ready.")
  ```

  ```typescript TypeScript theme={null}
  agent.onTaskComplete("waitlist", async (call: guava.Call) => {
    logger.info(
      "Added %s, party of %d, to waitlist.",
      await call.getField("caller_name"),
      await call.getField("party_size"),
    );
    await call.hangup("Thank the caller and let them know we'll text when their table is ready.");
  });
  ```
</CodeGroup>

## Start the agent

Attach the agent to a channel to start receiving inbound calls.

<CodeGroup>
  ```python Python theme={null}
  # Run this to attach your agent to a phone number. Call your agent's number to talk to it.
  agent.listen_phone(os.environ["GUAVA_AGENT_NUMBER"])

  # Run this to receive a WebRTC link where you can talk to your agent in the browser.
  agent.listen_webrtc()

  # Run this to talk to your agent using your local audio device.
  agent.call_local()

  # Run this to test your agent in a text-based chat session in the terminal (no audio required).
  agent.chat()
  ```

  ```typescript TypeScript theme={null}
  // Run this to attach your agent to a phone number. Call your agent's number to talk to it.
  agent.listenPhone(process.env.GUAVA_AGENT_NUMBER!);

  // Run this to receive a WebRTC link where you can talk to your agent in the browser.
  agent.listenWebrtc();

  // Run this to talk to your agent using your local audio device.
  agent.callLocal();

  // Run this to test your agent in a text-based chat session in the terminal (no audio required).
  agent.chat();
  ```
</CodeGroup>

## Complete example

<CodeGroup>
  ```python Python theme={null}
  import os
  import guava
  import logging
  import argparse
  from guava import logging_utils

  logger = logging.getLogger("thai_palace")

  agent = guava.Agent(
      name="Mia",
      organization="Thai Palace",
      purpose="Helping callers join the restaurant waitlist",
  )

  @agent.on_call_received
  def on_call_received(call_info: guava.CallInfo) -> guava.IncomingCallAction:
      return guava.AcceptCall()

  @agent.on_call_start
  def on_call_start(call: guava.Call) -> None:
      call.set_task(
          "waitlist",
          objective="You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
          checklist=[
              guava.Field(key="caller_name", field_type="text", description="Name for the waitlist"),
              guava.Field(key="party_size", field_type="integer", description="Number of people"),
              guava.Field(
                  key="phone_number",
                  field_type="text",
                  description="Phone number to text when the table is ready",
              ),
              "Read the phone number back to the caller to confirm.",
          ],
      )

  @agent.on_task_complete("waitlist")
  def on_waitlist_done(call: guava.Call) -> None:
      logger.info(
          "Added %s, party of %d, to waitlist.",
          call.get_field("caller_name"),
          call.get_field("party_size"),
      )
      call.hangup("Thank the caller and let them know we'll text when their table is ready.")

  if __name__ == "__main__":
      logging_utils.configure_logging()

      parser = argparse.ArgumentParser()
      group = parser.add_mutually_exclusive_group(required=True)
      group.add_argument("--phone", action="store_true", help="Listen for phone calls.")
      group.add_argument("--webrtc", action="store_true", help="Create on a WebRTC code.")
      group.add_argument("--local", action="store_true", help="Start a local call.")
      group.add_argument("--chat", action="store_true", help="Start a text-based chat session for testing.")
      args = parser.parse_args()

      if args.phone:
          agent.listen_phone(os.environ["GUAVA_AGENT_NUMBER"])
      elif args.webrtc:
          agent.listen_webrtc()
      elif args.chat:
          agent.chat()
      else:
          agent.call_local()
  ```

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

  const logger = getDefaultLogger();

  const agent = new guava.Agent({
    name: "Mia",
    organization: "Thai Palace",
    purpose: "Helping callers join the restaurant waitlist",
  });

  agent.onCallReceived(async (_callInfo: guava.CallInfo) => {
    return { action: "accept" };
  });

  agent.onCallStart(async (call: guava.Call) => {
    await call.setTask({
      taskId: "waitlist",
      objective: "You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
      checklist: [
        guava.Field({ key: "caller_name", fieldType: "text", description: "Name for the waitlist" }),
        guava.Field({ key: "party_size", fieldType: "integer", description: "Number of people" }),
        guava.Field({
          key: "phone_number",
          fieldType: "text",
          description: "Phone number to text when the table is ready",
        }),
        "Read the phone number back to the caller to confirm.",
      ],
    });
  });

  agent.onTaskComplete("waitlist", async (call: guava.Call) => {
    logger.info(
      "Added %s, party of %d, to waitlist.",
      await call.getField("caller_name"),
      await call.getField("party_size"),
    );
    await call.hangup("Thank the caller and let them know we'll text when their table is ready.");
  });

  const args = process.argv.slice(2);
  if (args.includes("--webrtc")) {
    agent.listenWebrtc();
  } else if (args.includes("--phone")) {
    agent.listenPhone(process.env.GUAVA_AGENT_NUMBER!);
  } else if (args.includes("--local")) {
    agent.callLocal();
  } else if (args.includes("--chat")) {
    agent.chat();
  } else {
    console.error("Usage: guava-example restaurant-waitlist --phone | --webrtc | --local | --chat");
    process.exit(1);
  }
  ```
</CodeGroup>


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