Qontak | Chatbot | AI Agent — Native Integrations — Phase 2: Calendly In-Conversation Booking
Calendly provider — Phase 2 PRD under the Native Integration ANCHOR. Calendly is an OAuth provider of the Native Integration umbrella; it reuses the Phase 1 (Google Calendar) OAuth + action-drawer pattern. Imported from Confluence on 2026-06-17. Code status: not yet started in production — PRD draft; depends on the shared OAuth + action runtime delivered by Phase 1.
HEADER BLOCK
| Field | Value |
|---|---|
| PM | Dimas Fauzi Hidayat |
| PRD Version | 1.1 |
| Status | DRAFT |
| PRD Type | PHASE |
| Epic | BOT-XXXXX — add once Epic is created |
| Squad | BOT — Chatbot & AI Squad |
| RFC Link | RFC — Calendly Action — to be created |
| Figma Master | Figma — ✨ Bot - AI (Action drawer, shared with Google Calendar) |
| Anchor | Native Integration — ANCHOR (Confluence) |
| Labels | epic:qontak-chatbot | module:ai-agent | feature:native-integrations-calendly |
| Last Updated | 2026-06-05 |
Status values: DRAFT → READY → BUILD → SHIPPED. READY gate: Epic cannot move to In Progress in Jira without PRD Link + RFC Link populated.
✅ Slotted (2026-06-05): Added to the ANCHOR Phase Index as Phase 2 (Google Sheets → Phase 3, Salesforce → Phase 4, Zoho → Phase 5, others → Phase 6+). Figma reuses the shared ✨ Bot - AI Action drawer frame (node 16639-192707) — identical to Google Calendar. Open Questions Q1 (Figma) and Q2 (slotting) closed.
2. CONDITIONAL BLOCK: PHASE CONTEXT
Anchor PRD: Native Integrations — ANCHOR (page 51173589337, v1.3 ACTIVE)
Phase Number: Phase 2 of 6 (provisional total). Slotted into the ANCHOR Phase Index
2026-06-05. Google-family phases renumbered: Google Sheets → Phase 3,
Salesforce → Phase 4, Zoho → Phase 5, Gmail/Drive/Docs/Outlook/Apple → 6+.
Phase Goal: Enable Qontak chatbot builders to connect their AI Agent to a Calendly
account so the agent can let end customers self-book meetings end-to-end
inside customer conversations — using Calendly's own availability and
scheduling engine (not direct calendar writes).
(Copied verbatim into the ANCHOR Phase Index Phase 2 row.)
Prior phases: Phase 1 — Google Calendar, In-Conversation Booking (PRD page 51173425496,
DRAFT score 9.7+/10). Delivered the OAuth connection flow and the
per-action details form pattern, action surfaced as a row in the existing
`Other integration` drawer group. Phase 1 books via direct Google Calendar
writes (calendar.events scope).
Deferred to next: Round-robin / collective Calendly event types; Calendly Routing Forms;
paid-event (Stripe/PayPal) bookings; reschedule & cancel from chat;
agent-initiated (human agent) booking on the customer's behalf.
Cross-phase deps: Reuse the canonical Action drawer (single-column, right-side, Figma file
LJ6ePL0PjxKbHYZZdNK4LX "Bot - AI", node 16639-192707) and add Calendly as a
new ROW in the existing `Other integration` group (alongside API Integrations,
Get nearest location, Google Calendar). Reuse the OAuth connection-storage
pattern and action-execution runtime from Phase 1 without breaking Google
Calendar. Calendly is a new action row, NOT a new drawer group and NOT a
parallel hierarchy (per ANCHOR S1 IA Surface + S8a 2026-05-22).
3. One-liner + Problem
One-liner: Add Calendly as a native AI Agent action so end customers can self-book meetings, using their team's existing Calendly availability, without leaving the chat.
Problem: Many Qontak customers run their scheduling on Calendly, not Google Calendar — so the Phase 1 Google Calendar action doesn't fit their workflow, and their AI Agent still has to push booking off-channel ("here's our Calendly link, click to book"). That handoff drops conversion: the customer leaves the conversation, the agent loses context, and there is no record of the booking back in Qontak. After this phase, a customer holding the Native Integrations add-on can connect a Calendly account to an AI Agent and let the customer pick a real, available slot end-to-end inside the conversation. For full initiative context, see the Native Integrations ANCHOR PRD.
3b. What Happens If We Don't Ship This Phase
- Customers standardised on Calendly cannot adopt the AI Agent booking capability at all — they are structurally excluded from Phase 1, so the initiative's booking-adoption metric is capped well below target until Calendly ships.
- The "share a link, book elsewhere" workaround persists: booking conversion stays low and bookings remain invisible to Qontak (no event logged), weakening the AI Agent's measurable ROI story for renewals.
- Competitive exposure: chatbot platforms positioning "book inside the chat" increasingly support Calendly natively; staying Google-Calendar-only narrows our deal fit in scheduling-heavy segments (clinics, agencies, professional services).
4. Target Users + Persona Context
Same persona set as the ANCHOR / Phase 1, adapted for Calendly. Full backstory: ANCHOR Section 2.
Primary Persona: CS Ops Lead / Bot Manager (ANCHOR Persona B)
Role: Customer-side CS Ops Lead or Bot Manager who owns AI Agent quality and
day-to-day configuration. Industry-agnostic.
Goal: Convert high-intent inbound conversations into a confirmed booking inside the
chat, using the team's existing Calendly availability.
Pain: Phase 1 only supports Google Calendar; their team schedules on Calendly, so the
"qualify but don't close" gap persists.
Workaround: Pastes a static Calendly link into a canned bot reply — no availability check,
no confirmation back in chat, no booking record.
Secondary Persona: Qontak Admin, Non-Tech (ANCHOR Persona A)
Role: General Qontak admin at SME / mid-market, frequently non-technical, single-hat.
Goal: "I bought Qontak — my AI Agent should talk to our Calendly without me reading
API docs." Connect Calendly and pick an event type with no OAuth/token knowledge.
Pain: The existing API Integration action assumes API-doc literacy; non-tech admins
don't attempt it, so the conversion moment never gets automated.
Workaround: Most don't attempt; a few escalate to support.
Runtime Beneficiary: End Customer (Booker)
Role: The person chatting with the AI Agent (WhatsApp / web chat) who wants to book.
Goal: Pick a working time and get a confirmed booking without leaving the conversation.
Pain: Today they get a bare link, bounce to a browser, often abandon; no confirmation
returns to the thread.
Workaround: Opens the link in a browser, books there (or drops off); conversation goes stale.
5. Non-Goals
- Reschedule and cancel from chat — booking only this phase. Managing an existing booking is deferred.
- Agent-initiated booking — human agent booking on the customer's behalf from the Inbox is out of scope; AI-Agent-driven self-service only. (Deferred.)
- Round-robin / collective / group event types — only single-host one-on-one Event Types this phase. Team distribution deferred.
- Paid bookings — Calendly events requiring Stripe/PayPal payment are not supported this phase.
- Calendly Routing Forms / Salesforce routing — pre-booking qualification routing is out of scope.
- Building a Qontak-native scheduling engine — availability, conflict resolution, time-zone math are owned by Calendly; we do not replicate them. (Never building.)
- Two-way calendar sync into Qontak CRM — writing the booking as a CRM Deal/Activity record is out of scope here (possible later CRM-squad SUPPORT PRD).
6. Constraints
Platform: Bot AI web console for setup (latest Chrome, Edge, Firefox, Safari).
Customer-facing booking renders in Qontak channels supporting interactive
messages / link cards (WhatsApp, web chat). Channels without rich rendering
fall back to a text link + slot list.
Performance: Event Type list ≤ 2s after connect. Availability fetch shown to customer ≤ 3s.
Action-config save ≤ 500ms. Booking confirmation in chat ≤ 5s after confirm.
Data limits: Up to 25 Event Types selectable per connected account per agent. One connected
Calendly organization per AI Agent this phase. OAuth tokens encrypted at rest;
no calendar contents stored — only booking references (event/invitee URI, time).
Entitlement: Compound — requires the AI Agent feature AND the single "Native Integrations"
add-on SKU (one SKU covers all providers; ANCHOR S8a 2026-05-06). Sold on
Enterprise/Advanced. Not on Starter/Growth. Calendly has no own SKU. Also
requires a paid Calendly plan exposing the needed Event Type (free tier = 1).
Brand assets: Calendly brand mark (logo SVG + hex) MUST be supplied as a literal,
code-gen-ready asset before any wireframe/MCP Pixel prompt is published.
Placeholder names (MpIcon name="calendar") are FORBIDDEN. Until provided, use a
guarded slot [CALENDLY_BRAND_SVG_PENDING — DO NOT CODE-GEN WITHOUT REPLACING]
(ANCHOR S8a 2026-05-22 hard rule). Tracked in Open Q4.
Feature flag: ai_agent_calendly_action | default: OFF. Enabled per account by Qontak ops for
customers holding the Native Integrations add-on; visible only to admins.
Read/write: Configure/connect/disconnect — Admin only. Trigger a booking — end customer
via the AI Agent. View booking events — Admin + Supervisor in Bot AI analytics.
No role can edit a customer's resulting Calendly booking from Qontak this phase.
6.7 Data Lifecycle
| Artifact Type | Retention Period | Cleanup Trigger | User-Visible Effect |
|---|---|---|---|
| Calendly OAuth tokens (access + refresh) | Until admin disconnects or revokes in Calendly | Admin clicks Disconnect, or token refresh fails permanently | Action shows "Disconnected"; agent stops offering booking until reconnected |
| Booking reference (event URI, invitee URI, scheduled time) | 24 months, then purged | Scheduled retention job (24-month TTL) | Booking disappears from Bot AI analytics after 24 months; booking itself lives in Calendly |
| Cached Event Type list | 15 minutes (soft cache) | TTL expiry or admin manual refresh | Stale event types possible for ≤15 min; refresh forces re-fetch |
| Failed booking attempt log | 90 days | Scheduled retention job | Observability/debugging; not customer-visible |
7. New Features
Calendly is a new ROW in the existing
Other integrationdrawer group — not a new drawer group, not a parallel surface (ANCHOR S1 IA Surface + S8a 2026-05-22). The Action drawer is Figma-canonical (single-column, right-side; file LJ6ePL0PjxKbHYZZdNK4LX, node 16639-192707). Only the per-action details form is greenfield (MCP Pixel).
Feature: Calendly action row + Calendly details form (within AI Agent → Add action)
Surface: AI Agent → Add action → right-side single-column Action drawer →
`Other integration` group → new "Calendly" row (siblings: API Integrations,
Get nearest location, Google Calendar). Selecting it opens the details form.
Access: Admin only (Native Integrations add-on + flag ON). End customer interacts at
runtime in-conversation, not in this console.
Drawer placement (Figma-canonical — do NOT redesign):
Action drawer (single-column, right-side — Figma LJ6ePL0PjxKbHYZZdNK4LX, node 16639-192707)
├── Mekari Qontak (group)
├── Mekari Talenta / Jurnal / Desty (groups)
└── Other integration (group)
├── API Integrations (existing)
├── Get nearest location (existing)
├── Google Calendar (Phase 1)
└── Calendly ← NEW ROW (brand mark + name + short desc)
Drawer states reused as-is: Collapse/expand · Scrolled · Search found · Search not found.
Per-action details form (Pixel MCP greenfield — Calendly variant of the shared form):
CalendlyDetailsForm
├── CalendlyConnectionField — OAuth connect / connected / disconnect (reuses Phase 1)
├── EventTypeSelect — dropdown of the account's single-host Event Types
├── BookingTriggerConfig — "When should the agent offer booking?" (reuses Phase 1)
├── PrefillMappingRows — map conversation/contact fields → invitee name/email/Qs
└── ConfirmationMessageField — message the agent sends after a confirmed booking
Runtime (in-conversation) surface:
SlotOfferMessage — interactive message listing N available slots (or date picker)
BookingConfirmationMessage — confirmation card with date/time, event name, details link
UI States (details form + runtime):
Empty: Form before connect → "Connect your Calendly account to choose an event type."
Loading: Event Type list → 3× skeleton rows. Runtime → "Checking available times…".
Error: Connect fails → inline banner + Retry. Availability fails at runtime → static link
+ apology (logged).
Success: Connected → "Connected as {email}" + event type selectable. Runtime → confirmation
card; admin sees a logged booking event.
Figma: Drawer + Calendly row = Figma-canonical, shared with Google Calendar —
https://www.figma.com/design/LJ6ePL0PjxKbHYZZdNK4LX/%E2%9C%A8-Bot---AI?node-id=16639-192707
(✨ Bot - AI, node 16639-192707). Calendly reuses this exact drawer frame; only the
row brand mark + label differ. Per-action details form = MCP Pixel greenfield.
Calendly brand SVG = guarded slot until provided (Section 6 Brand assets).
10. API & Webhook Behavior
Plain language — technical fields resolved during RFC.
Behavior 1: Connect Calendly account (OAuth)
Entity affected: Calendly connection record for this AI Agent (new)
Triggered by: Admin clicks "Connect Calendly"
Information passed: OAuth grant; store encrypted access + refresh tokens + account/org URI
Expected behavior: - Admin redirected to Calendly consent, returns to Qontak
- Shows "Connected as {email}"; Event Types become fetchable
Failure behavior: - Consent denied → no connection, inline error + Retry
- Token exchange fails → "Couldn't connect to Calendly, try again"
- Insufficient Calendly scope/plan → explain required Calendly plan
Behavior 2: Fetch Event Types
Entity affected: Read-only list of the connected account's Event Types
Triggered by: Action config load (after connect) + manual "Refresh"
Expected behavior: - Return active single-host one-on-one Event Types (name, duration, URI)
- Filter out round-robin/collective/group types
Failure behavior: - API error → keep last cached list (≤15 min) + "Couldn't refresh"
- Empty result → "No eligible event types found in this Calendly account"
Behavior 3: Fetch availability (runtime)
Entity affected: Read-only availability for the configured Event Type
Triggered by: AI Agent offers booking per trigger config
Information passed: Event Type URI; customer time zone; date range
Expected behavior: - Return next available slots in the customer's time zone
- Render as interactive message (or text fallback)
Failure behavior: - No slots → "No times available soon" + static link
- API/timeout → static Calendly link + apology, log failure
Behavior 4: Create booking (runtime)
Entity affected: A scheduled Calendly event + a Qontak booking reference
Triggered by: Customer selects a slot and confirms
Information passed: Event Type URI, slot, invitee name/email, mapped custom-question answers
Expected behavior: - Booking created in Calendly (Calendly sends its own confirmation email)
- Agent posts confirmation card; Qontak stores reference + emits event
Failure behavior: - Slot taken (race) → "That time was just taken" + re-offer
- Invalid/missing invitee email → agent asks the customer
- Booking API error → apologise, static link, log; no duplicate/partial
booking (idempotent retry by request key)
Behavior 5: Booking lifecycle webhook (inbound from Calendly)
Entity affected: Qontak booking reference status
Triggered by: Calendly invitee.created / invitee.canceled webhook
Expected behavior: - invitee.created confirms/enriches the booking reference
- invitee.canceled marks reference canceled (analytics only this phase)
Failure behavior: - Unverifiable signature → reject and log
- Unknown event URI → ignore safely
11. System Flow + User Stories + ACs
All ACs use strict Given/When/Then Gherkin. Each story carries a MoSCoW Priority + Before/After delta.
11.1 System Flow
Flow: Connect Calendly, then customer self-books in conversation
SETUP (Admin, one-time)
1. Admin opens AI Agent → Add action → Other integration group.
2. Admin selects the "Calendly" row.
3. Admin clicks "Connect Calendly" → Calendly OAuth consent.
4. Calendly returns grant → Qontak stores encrypted tokens → "Connected as {email}".
5. Qontak fetches eligible single-host Event Types → Admin picks one.
6. If fetch fails → cached list (≤15 min) or "Couldn't refresh" + Retry.
7. Admin maps invitee fields, writes the confirmation message, sets the trigger → Save.
RUNTIME (End customer, in conversation)
8. AI Agent reaches the trigger → fetches availability in the customer's time zone.
9. If fetch fails/timeout → static Calendly link + apology, log calendly_booking_failed.
10. Agent renders slots (interactive or text fallback).
11. Customer selects a slot and confirms.
12. Agent ensures invitee email is known (prefilled, else asks).
13. Qontak creates the booking in Calendly with an idempotency key.
14. If slot taken since shown (race) → say so and re-offer slots (back to 10).
15. On success → invitee.created webhook → confirm reference, post confirmation card,
emit calendly_booking_created.
16. If booking API errors → apologise, static link, log; no duplicate booking.
11.2 User Stories
Story CALENDLY-PH2-S01 — Admin connects a Calendly account to the AI Agent · Priority: Must Have Critical-path entry point; without a connected account no booking is possible.
Before state: The `Other integration` group lists API Integrations, Get nearest location,
Google Calendar. No Calendly row; no way to connect a Calendly account.
After delta: A "Calendly" row appears in `Other integration`; selecting it opens a details
form with a "Connect Calendly" OAuth flow storing an encrypted connection
scoped to this AI Agent.
As an AI Agent Builder (Admin), I want to connect our Calendly account to an AI Agent,
so that the agent can offer real Calendly availability inside conversations.
Acceptance Criteria
AC-1: Given an admin whose account holds the Native Integrations add-on with
ai_agent_calendly_action ON,
When they open Add action,
Then a "Calendly" row appears in the `Other integration` group with the Calendly brand
mark and a short description.
AC-2: Given the admin selected the Calendly action and is not yet connected,
When they click "Connect Calendly",
Then they are redirected to Calendly OAuth consent,
AND on approval they return to a "Connected as {calendly_account_email}" state.
AC-3: Given a successful connection,
When the action config loads,
Then encrypted access + refresh tokens are stored scoped to this agent,
AND no Calendly calendar contents are persisted beyond connection identity.
AC-4: Given a connected account,
When the admin clicks "Disconnect",
Then the stored tokens are deleted,
AND the action returns to the not-connected empty state,
AND the agent stops offering booking until reconnected.
ERR-1: Given the admin is in the Calendly OAuth flow,
When they deny consent or the token exchange fails,
Then no connection is stored, AND an inline error banner + Retry is shown,
AND calendly_connect_failed is logged with the reason.
ERR-2: Given the connected Calendly plan lacks the scope/plan for the needed Event Types,
When the connection completes,
Then a message explains the required Calendly plan,
AND the event-type list shows an eligibility empty state.
Permission Model: CAN: Admin (Native Integrations add-on, flag ON). CANNOT: Supervisor, Agent, end customer. Unauthorized: Calendly row not rendered without the add-on / for non-admins; no 403. UI States: Loading "Connecting…" · Empty "Connect your Calendly account…" · Error inline banner + Retry · Success "Connected as {email}" + selector enabled. Data Fields: calendly_account_email (Calendly OAuth) · access/refresh_token (encrypted) · organization_uri (OAuth) · agent_id (URL param). Figma: ✨ Bot - AI drawer (shared) — node 16639-192707. Dependencies: None (reuses Phase 1 OAuth pattern).
Story CALENDLY-PH2-S02 — Admin selects an Event Type and configures the booking action · Priority: Must Have Agent cannot offer booking without a chosen Event Type and mapping.
Before state: A Calendly account can be connected (S01) but the agent has no Event Type to
offer and no field mapping/confirmation message.
After delta: Admin selects one single-host Event Type, maps invitee name/email, sets a
trigger and confirmation message, and saves a runnable booking action.
As an AI Agent Builder (Admin), I want to choose a Calendly event type and configure how
booking is offered, so that the agent offers the right meeting with details prefilled.
Acceptance Criteria
AC-1: Given a connected account,
When the action config loads,
Then a dropdown lists active single-host one-on-one Event Types (name + duration),
AND round-robin/collective/group types are excluded.
AC-2: Given the admin selected an Event Type,
When they map invitee fields,
Then they can bind invitee name and email to contact/conversation fields,
AND can map answers to the Event Type's custom questions where present.
AC-3: Given mapping and a confirmation message are set,
When the admin clicks Save,
Then the action is persisted as runnable in ≤ 500ms,
AND the booking trigger condition is stored.
AC-4: Given more than 25 eligible Event Types exist,
When the dropdown loads,
Then at most 25 are listed with search,
AND the admin can search by name to find one beyond the first 25.
ERR-1: Given the Event Type fetch fails,
When the config loads,
Then the last cached list (≤15 min) is shown with "Couldn't refresh" + Retry,
AND if no cache exists an error empty state + Retry is shown,
AND calendly_eventtypes_fetch_failed is logged.
ERR-2: Given the admin tries to Save without an Event Type or invitee-email mapping,
When they click Save,
Then Save is blocked with inline validation naming the missing field,
AND no partial action is persisted.
Permission Model: CAN: Admin (add-on, flag ON). CANNOT: Supervisor, Agent, end customer. Unauthorized: config not rendered. UI States: Loading 3× skeleton rows · Empty "No eligible event types found…" · Error "Couldn't refresh" + Retry · Success selected + Save enabled. Data Fields: event_type_uri (Event Types API) · invitee_name_mapping · invitee_email_mapping · trigger_condition · confirmation_message. Figma: ✨ Bot - AI details form (Pixel MCP greenfield) — node 16639-192707. Dependencies: CALENDLY-PH2-S01.
Story CALENDLY-PH2-S03 — End customer self-books a slot inside the conversation · Priority: Must Have The end-customer value the phase exists to deliver.
Before state: The agent can only paste a static Calendly link; no in-chat availability,
selection, or confirmation; no booking record in Qontak.
After delta: The agent fetches real availability, the customer picks a slot in-chat, the
booking is created in Calendly, and a confirmation card is posted in the thread.
As an End Customer chatting with the AI Agent, I want to pick an available time and confirm
it in the conversation, so that I get a confirmed booking without leaving the chat.
Acceptance Criteria
AC-1: Given a configured Calendly action and the trigger is met,
When the agent offers booking,
Then available slots for the Event Type are shown in the customer's time zone ≤ 3s,
AND rendered as an interactive message where the channel supports it.
AC-2: Given slots are shown,
When the customer selects a slot and the invitee email is already known,
Then the booking is created in Calendly with an idempotency key,
AND a confirmation card (event name, date/time, details link) is posted ≤ 5s,
AND calendly_booking_created is emitted.
AC-3: Given the invitee email is not known,
When the customer selects a slot,
Then the agent asks for the email before creating the booking,
AND proceeds once a syntactically valid email is provided.
AC-4: Given the channel does not support interactive messages,
When the agent offers booking,
Then slots are presented as a numbered text list,
AND the customer can reply with the number to select.
ERR-1: Given slots were shown,
When the customer confirms a slot taken in the meantime,
Then no duplicate/partial booking is created,
AND the agent says the time was just taken and re-offers current availability,
AND calendly_slot_conflict is logged.
ERR-2: Given the availability fetch or booking call fails or times out,
When the customer is trying to book,
Then the agent sends the static Calendly link with a short apology,
AND calendly_booking_failed is logged with the reason,
AND the conversation is left clean (no half-booking).
Permission Model: CAN: End customer in an active conversation with the configured agent. CANNOT: admins/agents do not book here this phase. Unauthorized: if the action/flag is off, the agent never offers booking. UI States: Loading "Checking available times…" · Empty "No times available soon" + static link · Error apology + static link · Success confirmation card. Data Fields: event_type_uri · selected_slot · invitee_email · invitee_timezone · idempotency_key. Figma: ✨ Bot - AI runtime booking message (shared) — node 16639-192707. Dependencies: CALENDLY-PH2-S02.
Story CALENDLY-PH2-S04 — Admin/Supervisor sees booking outcomes in Bot AI analytics · Priority: Should Have Valuable for ROI/monitoring; phase still delivers core value without it.
Before state: No record in Qontak of bookings made (or attempted) via the agent.
After delta: Booking created/failed/canceled events are recorded and visible to
Admin/Supervisor, tied to agent and conversation.
As an AI Agent Builder or Supervisor, I want to see bookings created, failed, or canceled,
so that I can measure impact and spot problems.
Acceptance Criteria
AC-1: Given bookings have occurred,
When an Admin/Supervisor opens the agent's analytics,
Then counts of booking_created, booking_failed, booking_canceled are shown for a
selectable date range.
AC-2: Given a Calendly invitee.canceled webhook is received for a known booking,
When it is processed,
Then the booking reference is marked canceled,
AND the canceled count updates (no chat re-engagement this phase).
AC-3: Given a booking reference older than 24 months,
When the retention job runs,
Then the reference is purged from analytics,
AND aggregate historical counts are unaffected.
ERR-1: Given a webhook with an invalid signature or unknown event URI,
When it is received,
Then it is rejected/ignored safely,
AND no booking record is created or mutated,
AND calendly_webhook_rejected is logged.
Permission Model: CAN: Admin, Supervisor (add-on). CANNOT: Agent, end customer. Unauthorized: analytics section not rendered. UI States: Loading skeleton tiles · Empty "No bookings yet for this range." · Error "Couldn't load booking metrics" + Retry · Success metric tiles + counts. Data Fields: booking_event_type (enum) · agent_id · conversation_id · scheduled_time (optional). Figma: ✨ Bot - AI analytics (shared) — node 16639-192707. Dependencies: CALENDLY-PH2-S03.
Negative Scenarios (from Section 5 Non-Goals)
NEG-1: Given a confirmed booking exists (Non-Goal #1), when the customer asks to reschedule/
cancel in chat, then the agent does not do it and directs them to Calendly's own link.
NEG-2: Given an action is configured (Non-Goal #2), when a human agent tries to book on the
customer's behalf, then no agent-initiated booking surface is available this phase.
NEG-3: Given round-robin/collective Event Types (Non-Goal #3), when the admin opens the
dropdown, then those types are not selectable.
NEG-4: Given a payment-required Event Type (Non-Goal #4), when the customer reaches
confirmation, then the agent does not collect payment and such types are not offered.
11.3 Test Coverage Matrices (Must Have stories)
CALENDLY-PH2-S01: Boundary ⚠️ partial (QA: re-connect overwriting existing) · State transitions ✅ · Data validation ✅ · Concurrency ⚠️ TBD (two admins connect/disconnect same agent) · Network/timeout ✅. CALENDLY-PH2-S02: Boundary ✅ (>25 + search) · State transitions ✅ · Data validation ✅ · Concurrency ⚠️ TBD (event type deleted between fetch and save) · Network/timeout ✅. CALENDLY-PH2-S03: Boundary ✅ · State transitions ✅ · Data validation ✅ · Concurrency ✅ (slot-race + idempotency) · Network/timeout ✅.
11.4 Diagrams
System Flow:
sequenceDiagram
actor Admin
actor Customer
participant Bot as AI Agent
participant Q as Qontak Backend
participant C as Calendly API
Admin->>Bot: Add action -> Calendly -> Connect
Bot->>C: OAuth consent
C-->>Q: Grant -> store encrypted tokens
Q->>C: Fetch single-host Event Types
C-->>Admin: Event Types (select) + map + save
Note over Bot,Customer: Runtime
Customer->>Bot: Conversation hits booking trigger
Bot->>C: Fetch availability (customer TZ)
alt Availability OK
C-->>Customer: Show slots
Customer->>Bot: Select slot (+email if unknown)
Bot->>C: Create booking (idempotency key)
alt Slot still free
C-->>Q: invitee.created webhook
Q-->>Customer: Confirmation card + emit booking_created
else Slot taken (race)
C-->>Bot: Conflict
Bot-->>Customer: Just taken -> re-offer slots
end
else Fetch/booking fails
Bot-->>Customer: Apology + static link
Bot->>Q: log calendly_booking_failed
end
Admin config states:
stateDiagram-v2
[*] --> NotConnected: Select Calendly action
NotConnected --> Connecting: Click Connect
Connecting --> Connected: OAuth success
Connecting --> ConnectError: Denied / fail
ConnectError --> Connecting: Retry
Connected --> LoadingEventTypes: Fetch event types
LoadingEventTypes --> EventTypesLoaded: Types returned
LoadingEventTypes --> EventTypesEmpty: No eligible types
LoadingEventTypes --> FetchError: API error
FetchError --> LoadingEventTypes: Retry
EventTypesLoaded --> Saved: Select + map + Save
Connected --> NotConnected: Disconnect
Saved --> [*]
Runtime booking states:
stateDiagram-v2
[*] --> CheckingAvailability: Trigger met
CheckingAvailability --> SlotsShown: Slots <=3s
CheckingAvailability --> NoSlots: None in range
CheckingAvailability --> FetchFailed: Error/timeout
NoSlots --> StaticLink: Offer static link
FetchFailed --> StaticLink: Apology + static link
SlotsShown --> AskEmail: Slot selected, email unknown
SlotsShown --> Booking: Slot selected, email known
AskEmail --> Booking: Valid email
Booking --> Confirmed: Created in Calendly
Booking --> Conflict: Slot taken
Conflict --> SlotsShown: Re-offer
Booking --> StaticLink: Booking error
Confirmed --> [*]
StaticLink --> [*]
12. Rollout
Feature flag: ai_agent_calendly_action (Section 6 — OFF by default)
Rollout: Stage 1 → Internal QA: 2 Qontak test accounts + 1 test Calendly org
Stage 2 → Closed Beta: 5 design-partner Calendly-using add-on customers
Stage 3 → Open Beta: add-on customers who opt in via ops
GA → All Native Integrations add-on customers (flag per-account until
GA sign-off, then enabled for add-on customers)
Backward compat: Yes — additive. Phase 1 Google Calendar + all existing actions unaffected.
Calendly is a new row in the shared `Other integration` group.
Migration: None required — no existing data transformed.
13. Observability
| Event Name | Trigger | Properties |
|---|---|---|
| calendly_connected | Admin completes OAuth connect | agent_id, account_email, org_uri, timestamp |
| calendly_connect_failed | OAuth denied/exchange fails | agent_id, reason, timestamp |
| calendly_eventtypes_fetch_failed | Event Type fetch errors | agent_id, reason, cache_used, timestamp |
| calendly_booking_offered | Agent shows slots | agent_id, conversation_id, event_type_uri, channel, timestamp |
| calendly_booking_created | Booking confirmed | agent_id, conversation_id, event_type_uri, scheduled_time, timestamp |
| calendly_slot_conflict | Chosen slot already taken | agent_id, conversation_id, event_type_uri, timestamp |
| calendly_booking_failed | Availability/booking call fails | agent_id, conversation_id, reason, timestamp |
| calendly_webhook_rejected | Bad signature / unknown URI | reason, timestamp |
Dashboard owner: Chatbot & AI Squad (BOT)
Alerts:
- calendly_booking_failed rate > 10% of offered in 1h → page BOT on-call
- calendly_connect_failed > 5 in 15min for one account → notify BOT + CSM (Slack)
- calendly_eventtypes_fetch_failed with cache_used=false > 20 in 1h → notify BOT
13.5 Post-Launch Monitoring Cadence
Review cadence: Weekly for first 4 weeks post-GA, then monthly.
Owner: PM (Dimas) + BOT Squad lead.
Review scope: Booking conversion, booking_failed rate, connect success rate, adoption.
Trigger thresholds:
- booking_failed rate > 10% w/w → investigate within 48 hours
- booking conversion drops > 10% w/w for 2 consecutive weeks → PM review within 48h
Rollback consideration:
If calendly_booking_failed > 20% sustained 24h and traced to our integration (not Calendly
outage), PM toggles ai_agent_calendly_action OFF for affected accounts.
14. Success Metrics
Refine the ANCHOR S6b north-star metrics for the Calendly phase; targets aligned to roll up.
⭐ Primary KPI — Booking conversion inside chat (Calendly)
Definition: calendly_booking_created ÷ high-intent conversations that triggered the action
Baseline: 40–60% via human handoff today (ANCHOR assumption, unvalidated)
Target: ≥ 65% within 60 days of GA (matches ANCHOR S6b ⭐)
- Time-to-action: p50 from offer → booking success. Baseline minutes–hours. Target ≤ 30s.
- Add-on adoption: add-on customers configuring ≥1 Calendly action ÷ add-on customers.
Baseline 0. Target ≥ 50% within 60 days of GA.
- Action success rate: successful actions ÷ attempts (excl. Calendly outages). Target ≥ 98%.
- Slot-conflict rate: calendly_slot_conflict ÷ confirmations attempted. Target < 3%.
15. Launch Plan & Stage Gates
| Stage | Audience | Duration | Success Gate to Advance | Owner |
|---|---|---|---|---|
| Internal Alpha | 2 Qontak test accounts + 1 test Calendly org | 1–2 weeks | End-to-end connect→offer→book works across WA + web chat; booking_failed < 10% in test | PM + QA |
| Closed Beta | 5 Calendly-using add-on design partners | 3 weeks | ≥ 20 real bookings created; booking_failed < 8%; no P1 data/security issues | PM + CSM |
| Open Beta | Add-on accounts that opt in via ops | 3–4 weeks | Booking conversion ≥ 30% (interim); booking_failed < 6%; alerts stable | Eng Lead |
| GA | All Native Integrations add-on customers | Ongoing | Open Beta gates sustained 2 weeks; PMM approved; runbook + support docs ready | PM + PMM |
16. Dependencies
| Dependency | Owning Team | Deliverable Needed | Blocking? |
|---|---|---|---|
Figma-canonical Action drawer + Other integration group (file LJ6ePL0PjxKbHYZZdNK4LX, node 16639-192707) | BOT + Design | Must remain stable; Calendly added as a new row without breaking it | YES |
| Phase 1 OAuth connection storage pattern (encrypted token store) | BOT | Reuse for Calendly tokens; no breaking schema changes | YES |
| AI Agent action-execution runtime | BOT | Support a runtime action fetching availability + creating a booking mid-conversation | YES |
| Calendly API access (OAuth app, scopes, webhooks) | BOT + Calendly | Registered OAuth app, availability + scheduling API access, webhook subscription | YES |
| Channel interactive-message rendering (WA/web) | ONE / Channels | Render slot picker + confirmation card; text fallback where unsupported | YES |
| Native Integrations add-on SKU entitlement | Billing & Platform + Sales Ops | Enforce compound entitlement + flag; SKU sellable before Open Beta | YES |
| Analytics surface for booking events (S04) | BOT | Booking metrics in Bot AI analytics | NO (S04 is Should Have) |
17. Key Decisions + Alternatives Rejected
17a — Decisions Made
| Date | Decision | Rationale |
|---|---|---|
| 2026-06-04 | Use Calendly as the scheduling engine (availability + booking) rather than mirroring Phase 1's direct-calendar-write model | Calendly owns availability, conflict resolution, time-zone math; replicating it is wasteful and error-prone. Calendly-using customers want their event types respected. |
| 2026-06-04 | Surface Calendly as a new ROW in the existing Other integration drawer group, reusing the Figma-canonical single-column drawer | Matches ANCHOR S1 IA Surface + S8a 2026-05-22: native rows live under Other integration, not a new group or parallel page. Keeps Google Calendar intact. |
| 2026-06-04 | Request minimal Calendly OAuth scopes (read event types + availability, create scheduled events) | Mirrors ANCHOR S8b rejection of full scope; least-privilege, privacy-friendly. |
| 2026-06-04 | Scope this phase to booking only (no reschedule/cancel/agent-initiated) | Keeps the phase shippable; matches highest-value action; rest deferred. |
| 2026-06-04 | Single-host one-on-one Event Types only this phase | Avoids round-robin/collective complexity; covers majority of demand. |
| 2026-06-04 | Graceful fallback to the static Calendly link on any failure | Guarantees the customer can always book — no dead ends. |
| 2026-06-05 | Slotted as ANCHOR Phase 2; reuse the same Bot - AI Figma drawer frame as Google Calendar (node 16639-192707) | Active Calendly demand + shared OAuth/drawer pattern justify shipping ahead of the planned Google-family phases; shared frame keeps the two providers visually consistent. |
17b — Alternatives Rejected
| Alternative | Why Rejected | Date |
|---|---|---|
| Keep pasting the static Calendly link (no integration) | Off-channel drop-off, no confirmation, no record — the exact problem this solves | 2026-06-04 |
| Build a Qontak-native scheduling engine reading Calendly only for availability | Massive scope; duplicates Calendly; high double-booking/time-zone risk | 2026-06-04 |
| Generalise to a generic "calendar booking" abstraction over GCal + Calendly now | Premature; the two models differ (direct write vs hosted scheduling). Ship Calendly concretely first | 2026-06-04 |
| Support all Event Type kinds (incl. round-robin/collective) this phase | Team-distribution complexity for a minority of demand; deferred | 2026-06-04 |
18. Open Questions
| # | Type | Question | Owner | Deadline |
|---|---|---|---|---|
| 1 | Resolved (2026-06-05) | Figma frame confirmed: reuse the shared ✨ Bot - AI Action drawer frame (node 16639-192707), identical to Google Calendar — only the Calendly row brand mark/label differ. Per-action details form remains Pixel MCP greenfield | Design + PM | Closed |
| 2 | Resolved (2026-06-05) | Calendly slotted into the ANCHOR Phase Index as Phase 2; Google Sheets→3, Salesforce→4, Zoho→5, others→6+. Phase Goal copied verbatim into the index row | PM | Closed |
| 3 | Assumption | Calendly's API + minimal OAuth scopes expose single-host Event Types, availability, and scheduling sufficient for in-chat booking on the customer's Calendly plan | BOT Squad | 2026-06-13 |
| 4 | Open Question | Calendly brand asset (logo SVG + hex) supplied as a literal, code-gen-ready asset before any wireframe/MCP Pixel prompt is published — placeholder names forbidden (ANCHOR S8a 2026-05-22) | PM | 2026-06-13 |
| 5 | Assumption | Target customers hold a paid Calendly plan exposing the needed Event Type (free tier = 1) AND the Native Integrations add-on SKU is sellable | PM + CSM + Sales Ops | 2026-06-16 |
| 6 | Risk | Calendly API rate limits could throttle availability fetches at scale. Mitigation: soft-cache event types (≤15 min), batch availability windows, back off + static-link fallback on 429, alert on fetch-failure rate. | BOT Squad | 2026-06-18 |
| 7 | Risk | Slot shown then taken before confirm (race) could double-book/error. Mitigation: idempotency key on booking, re-offer on conflict (ERR-1), never leave a partial booking. | BOT Squad | 2026-06-18 |
| 8 | Risk | Storing OAuth tokens + booking references is a security/privacy surface. Mitigation: encrypt tokens at rest, store no calendar contents, scope per agent, support disconnect/purge, verify webhook signatures, 24-month TTL. | BOT Squad + Security | 2026-06-20 |
PRD CHANGELOG
| Version | Date | By | Section | Type | Summary |
|---|---|---|---|---|---|
| 1.0 | 2026-06-04 | Claude | All | CREATED | Calendly PHASE PRD created; Calendly-specific AI Agent action (hosted scheduling) under the Native Integrations ANCHOR. Reconciled to ANCHOR v1.2: Other integration drawer row, add-on SKU gating, north-star metrics aligned. |
| 1.1 | 2026-06-05 | Claude | Header, CB, S7, S11, S18 | MODIFIED | Slotted as ANCHOR Phase 2 (Sheets→3, Salesforce→4, Zoho→5, others→6+). Set Figma Master + all story/S7 Figma refs to the shared ✨ Bot - AI drawer frame (node 16639-192707), identical to Google Calendar. Closed Open Questions Q1 (Figma) and Q2 (phase slotting). |
| 1.2 | 2026-06-17 | Claude | Frontmatter | IMPORTED | Imported into the documents repo under native-integration/prds/ with repo frontmatter + anchor link. Body unchanged from Confluence v1.1. |