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

# Example: AI appointment booking

> How outside developers build an AI-powered appointment booking system on Trikon Voice.

This example is a booking product **you** run: a website or clinic backend that offers times, an AI agent that negotiates a slot on the phone, and a calendar that actually holds the event.

Trikon Voice supplies the phone call and (when you connect it) [Google Calendar tools](/user-guide/google-calendar) on the agent. Your app supplies the booking record, reminders, and UI.

## Architecture

Two complementary designs. You can use both.

| Design                                | Who talks                                       | Where the slot is stored                               |
| ------------------------------------- | ----------------------------------------------- | ------------------------------------------------------ |
| **A. Agent books on the call**        | Customer ↔ voice agent                          | Google Calendar on the agent, plus your DB via webhook |
| **B. Your app books, agent confirms** | Your API creates a hold; agent calls to confirm | Your DB is source of truth                             |

Most clinic and demo-booking apps start with **A**.

## Shared setup

1. Create an **outbound** agent (and optionally an **inbound** agent on your number) with booking instructions. See [Create your first bot](/user-guide/create-your-first-bot).
2. On that agent, **Integrations → Google Calendar → Authorize** so `get_availability` and `create_event` work on the live call.
3. Put calendar keywords in the system script (`google calendar`, `get_availability`, `create_event`) as described in the [Google Calendar guide](/user-guide/google-calendar).
4. Copy API key, workspace slug, agent ID, and **from** UUID. Set the webhook to your booking service.

## Design A — the agent books during the call

### System instructions (copy into the agent)

```markdown theme={null}
# Appointment booking
You can use Google Calendar tools: get_availability, create_event,
check_existing_events, reschedule_event, cancel_event.

When the caller wants an appointment:
1. Ask what the visit is for (if you do not already know).
2. Ask for a preferred date and time (interpret times in IST unless they specify).
3. Call get_availability for that window.
4. If busy, offer the next two free slots. Do not invent a free time.
5. Ask for full name and email.
6. Call create_event with start, end, summary, and attendee email.
7. Read back the confirmed time and say the invite is on its way.
8. If they decline every slot, offer a callback and do not create an event.
```

Pass known facts so the agent does not re-ask them:

```bash theme={null}
curl -X POST https://voice.trikon.tech/api/outbound-call \
  -H "Authorization: Bearer $TRIKON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+919845012345",
    "agentId": "YOUR_BOOKING_AGENT_ID",
    "enterprise": "acmeclinic",
    "from": "YOUR_PHONE_LINE_UUID",
    "name": "Asha",
    "metadata": {
      "clinic_name": "Acme Clinic",
      "visit_type": "health checkup",
      "patient_email": "asha@example.com"
    }
  }'
```

Use `{{clinic_name}}`, `{{visit_type}}`, and `{{patient_email}}` in the greeting and script.

### Your booking table

```sql theme={null}
CREATE TABLE appointments (
  id              UUID PRIMARY KEY,
  customer_name   TEXT,
  phone           TEXT NOT NULL,
  email           TEXT,
  slot_start      TIMESTAMPTZ,
  slot_end        TIMESTAMPTZ,
  status          TEXT NOT NULL DEFAULT 'pending', -- pending | booked | cancelled | no_show
  call_uuid       TEXT,
  transcript_note TEXT
);
```

The calendar event is created **on the call** by the agent. Your webhook still records it so the website can show “You’re booked”:

```ts theme={null}
async function onBookingWebhook(body: {
  event: string;
  callUuid: string;
  to: string;
  name?: string;
  intent?: string;
  summary?: string;
  transcript?: { speaker: string; text: string }[];
}) {
  if (body.event !== "analysis.completed" && body.event !== "call.completed") {
    return { ok: true };
  }

  const booked =
    body.intent === "appointment_scheduled" ||
    /booked|confirmed|scheduled/i.test(body.summary || "");

  await db.appointments.upsertByPhone(body.to, {
    customer_name: body.name,
    status: booked ? "booked" : "pending",
    call_uuid: body.callUuid,
    transcript_note: body.summary,
  });

  return { ok: true };
}
```

Parse date/time from `summary` or `transcript` if you need `slot_start` in SQL. Instruct the agent to always say the booking in a fixed shape, e.g. `Confirmed: 12 October 2026 15:00 IST`.

Webhook fields: [Webhooks](/developers/webhooks).

## Design B — your system holds the slot, the agent confirms

Use this when slots live in **your** scheduler (not only Google Calendar).

<Steps>
  <Step title="Customer picks a time on your site">
    Insert `appointments` with `status = pending`. Do not mark booked yet.
  </Step>

  <Step title="Trigger the confirmation call">
    `POST /api/outbound-call` with metadata:

    ```json theme={null}
    {
      "to": "+919845012345",
      "agentId": "YOUR_AGENT_ID",
      "enterprise": "acmeclinic",
      "from": "YOUR_PHONE_LINE_UUID",
      "name": "Asha",
      "metadata": {
        "slot_start": "12 October 2026, 3:00 PM IST",
        "visit_type": "health checkup",
        "clinic_address": "14 MG Road"
      }
    }
    ```
  </Step>

  <Step title="Agent script">
    “You are confirming an existing hold. Tell them the time in `{{slot_start}}`. If they say yes, thank them. If they want another time, note the new preference; do not invent a calendar event unless Google Calendar tools are enabled.”
  </Step>

  <Step title="Webhook">
    Map `intent` to `booked` vs `cancelled` vs `reschedule_requested` and update your scheduler (release the hold, create the real appointment, or open a new slot picker).
  </Step>
</Steps>

## Inbound: they call you to book

Put the **same** booking agent on an inbound number ([Phone numbers](/user-guide/phone-numbers)). No `outbound-call` is required. The webhook still fires when the call ends, so your `appointments` table stays in sync.

## End-to-end checks

<Steps>
  <Step title="Playground">
    Test the script in the browser first ([Test your bot](/user-guide/test-your-bot)).
  </Step>

  <Step title="Calendar">
    Call yourself, ask for a real slot, confirm a Google Calendar event and invite appear.
  </Step>

  <Step title="Your app">
    After hangup, `analysis.completed` should set `appointments.status = booked` (design A) or confirm the hold (design B).
  </Step>
</Steps>

<Warning>
  Calendar tools run only if that **agent** is connected to Google Calendar. Connecting a calendar on a different agent does not apply. Timezone for spoken times defaults to IST.
</Warning>

## Related

* [Make a call](/developers/make-a-call)
* [Google Calendar on the agent](/user-guide/google-calendar)
* [Voice CRM example](/examples/voice-crm) — use the same webhooks to drop booked callers into a **Booked** column
