Skip to main content

Qontak | AI Agent | Meta Business Agent Integration — Phase 1: MBA-Aware Foundation

HEADER BLOCK

FieldValue
PMDimas Fauzi Hidayat
PRD Version1.7
StatusDRAFT
PRD TypeNEW (Phase 1 of 3)
EpicTBD — add once Epic is created
SquadChatbot (BOT)
RFC LinkN/A — RFC to follow via rfc-starter after PRD is READY
Figma MasterPending — low-fi wireframe embedded in §6 (inbox control states); Figma TBD
AnchorYes — Qontak | AI Agent | Meta Business Agent Integration — ANCHOR
Labelsepic:qontak-chatbot | module:ai-agent | feature:meta-business-agent
Last Updated2026-07-18

Table of Contents


CONDITIONAL BLOCK: PHASE CONTEXT

FieldValue
Anchor PRDQontak | AI Agent | Meta Business Agent Integration — ANCHOR
PhasePhase 1 of 3
Phase Goal (verbatim from Anchor Phase Index)Make the Qontak inbox fully aware of MBA-run conversations — persist standby copies, track who controls each room, and let agents take over and hand back.
Prior phasesNone — this is the first phase.
Deferred to later phasesPhase 2: MBA onboarding/config UI on the channel page, MBA Graph management endpoints (eligibility/onboarding/allowlist/settings), SyncToMetaMba knowledge/persona sync, Resources/Actions sync columns. Phase 3: connector gateway (MBA executes Qontak-hosted actions), MBA agent test/eval integration.
Cross-phase dependenciesPhase 2 and 3 both consume this phase's room control-state model and standby persistence — the data foundation must be stable before management surfaces build on it.

Evidence note (research grounding): sources: [] — no direct user research exists on MBA adoption intent (product announced by Meta 2 Jul 2026). The problem is grounded in platform mechanics (Meta developer docs) and code grounding of hub-core/chatbot (2026-07-07), not customer interviews. Declared as a deliberate evidence gap; validate adoption intent with 3–5 design-partner conversations during the beta stage.

2. One-liner + Problem

One-liner: Make the Qontak inbox fully aware of Meta Business Agent conversations — every message persisted, control state visible, take-over and hand-back working.

Problem: When Meta Business Agent is enabled on a WhatsApp number, consumer messages and MBA's replies arrive on the standby webhook field and control changes arrive on messaging_handovers — and hub-core's webhook consumer routes neither, so those conversations are invisible or half-visible in the Qontak inbox. An agent who replies implicitly takes control on Meta's side, but Qontak has no Thread Control client, so the conversation can never be returned to MBA — the room is permanently human-owned after one reply. Any Qontak customer with an eligible WABA can switch MBA on today in WhatsApp Manager without Qontak's involvement, which means the first adopter turns their paid Qontak inbox into a partially blind console with no warning.


3. Target Users + Persona Context

PersonaRoleGoalPainWorkaround
Primary — CS AgentAgent handling WhatsApp conversations in the Qontak web inboxRead the full conversation (including MBA's replies), know who is driving, take over cleanly, and hand back when doneMBA-run rooms would show only fragments; no indicator of who controls the room; after one reply the agent owns the room foreverNone — asks the customer to repeat context; room never returns to the AI
Secondary — CS SupervisorSupervisor monitoring team workload and AI performance on WhatsAppSpot which rooms MBA is driving vs humans, and trust the transcript for review/QACannot distinguish MBA rooms in the room list; transcript gaps make after-the-fact review unreliableNone — cross-references WhatsApp Business app manually

(See Constraints for plan availability and feature flag scope.)


4. Non-Goals

  1. No MBA onboarding or configuration UI — eligibility check, onboarding, allowlist, settings, and the channel-page "Meta Business Agent" section are Phase 2.
  2. No knowledge/persona sync to MetaSyncToMetaMba, the Resources "Meta sync" column, and the Actions "Meta connector" column are Phase 2.
  3. No connector gateway — MBA executing Qontak-hosted actions is Phase 3.
  4. No MBA test/eval integration — running AiAgentTestCase against Meta's agent test/eval APIs is Phase 3.
  5. No changes to Qontak's own AI agent pipeline — the existing bot flow / AI agent behavior on non-MBA rooms is untouched; Qontak's AI must never respond on rooms MBA controls.
  6. No mobile app changes — web inbox only in this phase.
  7. No reporting/analytics changes — MBA containment reporting consumes this phase's data but ships under the AI ROI report initiative, not here.

Scope Changes

Engineering surfaces this PRD touches (controlled vocab). Kept in sync with the scope_changes frontmatter above.

  • Backendhub-core: route standby and messaging_handovers events in the Kafka webhook consumer (app/core/events/kafka_consumers/webhook_outboxes/incoming_consumer.rb channel_event_type switch); persist standby consumer messages + MBA echoes as first-class messages; new room control-state field sourced from handover events; new Thread Control (release) method on a new MBA API client (base api.facebook.com, versioned via X-API-Version header — sibling of, not extension to, the wa_cloud Graph client); suppress Qontak bot/AI responses on MBA-controlled rooms — suppression point is an RFC choice (ground-prd 2026-07-19): hub-core's message-interaction fan-out (repositories/messages/creates/customer.rb:80-91 — note it serves ALL webhook subscribers, not only the bot) vs the chatbot dispatch entry (ProcessIncomingMessageWorkerprocess_incoming_message_with_resolve.rb). chatbot: no changes this phase unless the RFC lands suppression chatbot-side.
  • Frontendhub-chat (inbox): conversation control banner with Take over action, "Meta Business Agent" sender label on echo messages, handover timeline markers, "Return to Meta agent" composer action, "Meta AI" room-list tag on the room list — ground-prd 2026-07-19: ChatList has no tag prop; it renders tags from room.tags data via the roomItemTags computed (ChatList.vue:253-278, 294), so the tag ships as a new prop or a data-side tag injection (RFC choice).
  • Design — Figma for the four inbox elements (field-accurate low-fi wireframes exist; Figma frames TBD — flagged in Header).

5. Constraints

FieldValue
PlatformWeb inbox only (hub-chat). Mobile deferred (Non-Goal 6).
PerformanceStandby message persisted and visible in inbox ≤ 5s from webhook receipt (p95). Control-state banner reflects a messaging_handovers event ≤ 2s after consumption (p95). Thread Control release round-trip feedback to the agent ≤ 3s (p95).
Data limitsStandby echoes stored as normal messages — same retention as all room messages. Handover events stored as room events, same retention as room history. No new retention class.
Plan scopeAll plans with a WhatsApp Cloud channel. This phase is a safety/visibility layer, not a monetized feature — gating it would recreate the blind-inbox problem for lower tiers.
Feature flagmba_aware_inbox | default: OFF. Enabled per organization. Flag OFF = current behavior (standby events consumed but only logged, no UI). Flag flips ON per org at beta, default ON at GA.
TenancyStandby/handover events must resolve to the owning organization via the existing ChannelIntegration webhook lookup — zero cross-tenant leakage tolerated (hard gate, see §12).
Data lifecycleStandby echoes and handover room-events follow existing message/room retention and deletion flows — deleted when the room is deleted; no orphaned artifacts. Unroutable standby events (no matching channel integration) go to the existing dead-letter path with a 14-day retention, then purged.

6. New Features

Surface: existing inbox conversation view (/inbox) — four additive elements, no new pages.

Inbox conversation (existing)
├── ControlBanner (NEW) — shown only when room has MBA control history
│ ├── State: agent-driving → "Meta Business Agent is handling this conversation" + [Take over]
│ └── State: app-driving → "You're in control" + [Return to Meta agent]
├── Message bubble (existing) — NEW sender type label: "Meta Business Agent" on standby echoes
├── Timeline marker (NEW) — centered pill on each control change:
│ "You took over — control passed to Qontak · {time} · {actor}" /
│ "Returned to Meta Business Agent · {time} · thread control pass"
└── Composer (existing) — NEW inline action "Return to Meta agent" (only when app holds control)

Room list (existing ChatList component)
└── Tag slot (existing prop) — NEW tag value "Meta AI" on rooms where MBA holds control

UI states:

  • Loading: banner and markers render with the room history load — no separate spinner.
  • Empty: rooms with no MBA history render exactly as today (zero visual change when flag ON but MBA unused).
  • Error: Thread Control release failure → banner unchanged (app still in control) + error toast "Couldn't return the conversation to Meta Business Agent. Try again." + retry affordance.
  • Success: control returned → banner switches to agent-driving state + timeline marker appended.

Wireframe — inbox control states (existing vs. new):

Phase 1 inbox wireframe: existing inbox layout with the new control banner, "Meta Business Agent" sender label, two handover timeline markers, and the "Return to Meta agent" composer action; the room list shows the new "Meta AI" tag.

Everything in solid grey is the current inbox (room-list panel, message bubbles, composer — the existing ChatList component and its room-tags rendering). The four dashed-blue elements are the entire Phase 1 FE build: the control banner, the "Meta Business Agent" sender type on standby echoes, the two timeline markers on control changes, and the "Return to Meta agent" composer action. Figma frames TBD (Dependency 4).

7. API & Webhook Behavior

Written in plain language per PRD convention — HTTP/JSON specifics belong to the RFC. Meta docs v2.0.0 (2026-07-18): Thread Control is POST /business/whatsapp/phone_numbers/{phone_number_id}/thread_control on api.facebook.com; action enum is pass | release, and only release functions today ("relinquishes thread control and hands the conversation back to Meta Business Agent") — pass is reserved for future use. All hand-back behavior in this PRD uses release.

#BehaviorEntity AffectedTriggered ByExpected BehaviorFailure Behavior
1Consume standby webhook — consumer messageRoom, MessageMeta delivers a consumer message while MBA holds controlResolve org/channel via existing webhook lookup; create the room if it doesn't exist (MBA answering a brand-new customer — room starts unassigned, MBA-controlled); persist the consumer message on the room timelineUnroutable event → dead-letter path + mba_standby_unroutable log; consumer retries per existing Kafka semantics — no message dropped silently
2Consume standby webhook — MBA echoMessageMeta delivers a copy of MBA's outbound replyPersist as a message with sender type "Meta Business Agent" so the transcript is complete; no notification to agents (observing, not acting)Same dead-letter + retry semantics as #1; duplicate echo (redelivery) is idempotent on the external message id
3Consume standby delivery/read receiptsMessage statusMeta delivers receipts for MBA's messagesUpdate message status so the inbox shows delivered/read consistently with normal roomsReceipt for unknown message → logged, skipped (no crash)
4Consume messaging_handoversRoom control state, Room eventControl of the conversation changes (either direction)Set the room's control state to the event's new controller (meta_agent or app); append a room event that renders as the timeline marker; this webhook is the source of truth for control stateOut-of-order/duplicate events reconciled by event timestamp — latest wins; unroutable → dead-letter + log
5Agent sends a message on an MBA-controlled roomRoom control state, MessageAgent replies in the inbox (normal send)Send via existing outbound path (Meta auto-transfers control to the app on send); optimistically set control state to app pending confirmation by the next messaging_handovers eventSend failure → existing send-error handling; control state NOT flipped (no handover happened)
6Return control to MBA (Thread Control release)Room control state, Room eventAgent clicks "Return to Meta agent"Call the new Thread Control method on the Graph client; on success set control state to meta_agent + append room event; confirm against the messaging_handovers event that followsGraph error → control state unchanged, error toast to agent, mba_release_control_failed log with Meta error code; retryable
7Suppress Qontak automation on MBA-controlled roomsBot/AI dispatchInbound message on a room whose control state is meta_agentQontak bot flow / AI agent must NOT fire (MBA is the responder; double-response is a customer-facing bug and violates one-responder-per-number)If control state is unknown (no handover event yet), default to suppression on rooms with any MBA history + mba_suppression_ambiguous log

Meta API surface & build map — this phase's slice (docs v2.0.0, fetched 2026-07-18):

Meta surfaceEndpoints & key facts (v2.0.0)Qontak buildsReusesWhere
WebhooksSubscribe via POST /{WABA_ID}/subscribed_appsboth existing subscribe calls send no field list (wa_embedded_signup/services/apis.rb:94-104, wa_cloud/services/apis.rb:63-70): the webhook field set (messages, standby, messaging_handovers) is Meta app-level configuration, so enabling the new fields takes effect for ALL WABAs at once and the consumer must tolerate them globally from day one (flag-OFF orgs log-only, per S06/AC-1)Route standby + messaging_handovers in the Kafka consumer; persist standby messages/echoes; room control_state from handover eventswa_cloud incoming_consumer.rb switch, existing message persistence, embedded-signup subscription flowS01–S03
Thread ControlPOST /business/whatsapp/phone_numbers/{phone_number_id}/thread_control on api.facebook.com; action ∈ {pass, release}only release functions (hand back to MBA; pass reserved); caller must hold control; to = consumer numberrelease client method + optimistic control-state flip confirmed by the follow-up handover eventnew MBA API client (below)S04–S05
MBA API client (cross-cutting, born this phase)Base api.facebook.com (not graph.facebook.com), versioned via X-API-Version header; Bearer auth = system-user/BISU token with whatsapp_business_messaging, or the bizai_wa_enterprise_api_3p_access tech-provider capability (Anchor OQ-2)New client as a sibling of, not extension to, the wa_cloud Graph client; Phases 2–3 add their endpoint groups onto itsystem-user token pattern (FACEBOOK_SYSTEM_USER_ACCESS_TOKEN / per-org override — matches Meta's BISU model; LOCKBOX keeps holding channel secrets), existing client retry/error patternsScope Changes

8. System Flow + User Stories

System Flow

  1. Customer sends a WhatsApp message to a number where MBA is enabled; Meta delivers it to MBA (primary responder) and to Qontak on the standby field.
  2. Qontak resolves the org/channel, creates the room if new, persists the consumer message; room control state = meta_agent; room list shows the "Meta AI" tag.
  3. MBA replies; Meta delivers a copy on standby; Qontak persists it labeled "Meta Business Agent"; receipts update its status.
  4. Agent opens the room: control banner reads "Meta Business Agent is handling this conversation"; transcript is complete.
  5. Customer raises something needing a human; agent clicks Take over (focuses composer) and sends a reply through the normal outbound path.
  6. Meta transfers control to the app; the messaging_handovers event confirms; Qontak sets control state = app, appends a "You took over" timeline marker; subsequent consumer messages now arrive on the messages field (existing path).
  7. Failure branch: if the send fails, control state does not flip and the existing send-error UX applies.
  8. Agent resolves the issue and clicks Return to Meta agent; Qontak calls Thread Control release.
  9. On success: control state = meta_agent, "Returned to Meta Business Agent" marker appended, banner reverts; MBA answers the next customer message.
  10. Failure branch: Thread Control error → toast + retry; the room stays app-controlled; mba_release_control_failed logged.
  11. Throughout: Qontak's own bot flow / AI agent never fires on rooms MBA controls (suppression check on every inbound dispatch).

System-flow diagram (User Journey — mirrors the numbered steps above, both failure branches included):

sequenceDiagram
autonumber
actor C as Customer (WhatsApp)
participant M as Meta / WhatsApp Cloud
participant MBA as Meta Business Agent
participant Q as Qontak (hub-core + inbox)
actor A as CS Agent

C->>M: Sends message
M-->>Q: standby field (consumer message)
Q->>Q: Resolve org, create room if new, persist — control = meta_agent — "Meta AI" tag
M->>MBA: Deliver to primary responder
MBA-->>M: Reply
M-->>Q: standby copy (MBA echo) + delivery/read receipts
Q->>Q: Persist echo labeled "Meta Business Agent"
A->>Q: Opens room (banner: "Meta Business Agent is handling this")
Note over A,Q: Customer raises something needing a human
A->>M: Take over — send reply (existing outbound path)
alt Send succeeds
M-->>Q: messaging_handovers (control = app)
Q->>Q: control = app — append "You took over" marker
C->>M: Follow-up message
M-->>Q: messages field (existing inbound path)
else Send fails
Q-->>A: Existing send-error UX — control NOT flipped
end
A->>Q: Click "Return to Meta agent"
Q->>M: Thread Control (release)
alt Pass succeeds
M-->>Q: messaging_handovers (control = meta_agent)
Q->>Q: control = meta_agent — append "Returned" marker
C->>MBA: Next message → MBA resumes
else Pass fails
Q-->>A: Error toast + retry — room stays app-controlled — log mba_release_control_failed
end
Note over Q: Suppression — Qontak bot/AI never fires while control = meta_agent

User Stories

User StoryImportanceMockupTechnical NotesAcceptance Criteria
[MBA-S01] — Persist standby conversations

As a CS agent, I want every message in an MBA-run conversation stored in the room, so that the transcript I see is complete.
Must Have— (backend)Data Fields:
external_message_id (string, required) — source: Meta webhook payload (idempotency key)
sender_type (enum, required: customer | meta_business_agent) — source: standby payload direction
channel_integration_id (fk, required) — source: existing webhook lookup

Before-After Behavior: Before: standby events are not routed — MBA conversations are invisible or fragmentary. After: consumer messages, MBA echoes, and receipts persist as first-class messages on the room.
— Happy Path —
• AC-1: Given MBA holds control of a conversation, when a consumer message arrives on the standby field, then it is persisted on the correct org's room within 5s (p95) and appears in the inbox timeline.
• AC-2: Given MBA sends a reply, when its echo arrives on standby, then it is persisted with sender type "Meta Business Agent" and its delivery/read receipts update its status.
• AC-3: Given no room exists for the consumer (MBA answering a first-contact customer), when the first standby event arrives, then a room is created unassigned with control state meta_agent and no agent notification fires.
• AC-4: Given Meta redelivers an event, when the same external_message_id arrives twice, then exactly one message exists (idempotent).

— Error / Unhappy Path —
• ERR-1: Given a standby event whose webhook resolves to no channel integration, when it is consumed, then it goes to the dead-letter path (14-day retention) and mba_standby_unroutable is logged with the WABA id — never silently dropped.

— Permission Model —
• CAN: system (webhook consumer)
• CANNOT: n/a — no user-triggered path

— UI States —
• Success: messages render in timeline order with existing bubble components.
[MBA-S02] — Track who controls the room

As a CS agent, I want the room to know whether Meta's agent or Qontak holds the conversation, so that I always know who is driving.
Must Have— (backend)Data Fields:
control_state (enum, required: meta_agent | app) — source: messaging_handovers payload
handover_event room event (actor, direction, timestamp) — source: webhook

Before-After Behavior: Before: no control-state concept exists on rooms; messaging_handovers is unhandled. After: every control change updates the room's control state and appends a room event; the webhook is the source of truth.
— Happy Path —
• AC-1: Given a messaging_handovers event transferring control to the app, when it is consumed, then the room's control state is app within 2s (p95) and a room event is appended.
• AC-2: Given a handover event transferring control back to Meta, when it is consumed, then control state is meta_agent and a room event is appended.
• AC-3: Given duplicate or out-of-order handover events, when consumed, then the event with the latest timestamp determines control state (no flapping).

— Error / Unhappy Path —
• ERR-1: Given a handover event for an unknown room, when consumed, then it is dead-lettered and mba_handover_unroutable is logged.

— Permission Model —
• CAN: system (webhook consumer)
• CANNOT: agents/admins cannot edit control state directly — it only changes via webhook or the S05 pass action.
[MBA-S03] — See MBA state in the inbox

As a CS agent, I want a control banner, sender labels, timeline markers, and a room-list tag on MBA conversations, so that I can read the room at a glance.
Must Have§6 wireframe — Figma TBDData Fields:
control_state (from room, S02)
sender_type (from messages, S01)
• handover room events (S02)

Before-After Behavior: Before: MBA rooms are indistinguishable from normal rooms with missing messages. After: banner shows who is driving, MBA messages are labeled, control changes render as timeline markers, and the room list shows a "Meta AI" tag rendered through ChatList's existing tags row (new prop or room-tags data injection — no tag prop exists today).
— Happy Path —
• AC-1: Given a room with control state meta_agent, when the agent opens it, then the banner reads "Meta Business Agent is handling this conversation" with a Take over action, and the room list shows the "Meta AI" tag.
• AC-2: Given a room with control state app, when opened, then the banner shows "You're in control" with a Return to Meta agent action, and the tag is absent.
• AC-3: Given handover room events exist, when the timeline renders, then each control change appears as a centered marker with actor and timestamp.

— Error / Unhappy Path —
• ERR-1: Given the control state cannot be determined (no handover event yet on a room with MBA history), when the room opens, then the banner shows the observing state (not "You're in control") and no automation fires (per S7 #7).

— Permission Model —
• CAN: any agent/supervisor with access to the room (read)
• CANNOT: roles without room access — elements not rendered

— UI States —
• Loading: renders with room history load
• Empty: rooms with no MBA history are visually unchanged
• Error: banner falls back to observing state
• Success: banner + labels + markers + tag consistent with control state
[MBA-S04] — Take over from MBA

As a CS agent, I want replying to take the conversation from Meta's agent, so that escalation is one action, not a procedure.
Must Have§6 wireframe — Figma TBDData Fields:
• outbound message via existing send path
• optimistic control_state = app pending webhook confirm (S02)

Before-After Behavior: Before: an agent reply on an MBA room behaves like any send, but Qontak has no record that control moved. After: send triggers optimistic control-state flip, confirmed by the messaging_handovers event; banner and markers update.
— Happy Path —
• AC-1: Given MBA holds control, when the agent sends a reply, then the message is delivered via the existing outbound path and the banner switches to "You're in control" optimistically.
• AC-2: Given the subsequent messaging_handovers event arrives, when consumed, then the optimistic state is confirmed and the "took over" timeline marker is appended with the agent's name.
• AC-3: Given the customer sends follow-ups after takeover, when they arrive on the messages field, then they flow through the existing (non-standby) inbound path unchanged.

— Error / Unhappy Path —
• ERR-1: Given the send fails, when the error returns, then control state does NOT flip, the existing send-error UX shows, and no marker is appended.

— Permission Model —
• CAN: agents assigned/with access to the room
• CANNOT: read-only roles — composer disabled as today
[MBA-S05] — Return the conversation to MBA

As a CS agent, I want to hand the conversation back to Meta's agent when I'm done, so that the AI resumes and my queue stays clean.
Must Have§6 wireframe — Figma TBDData Fields:
• Thread Control release call on the new MBA API client (new method)
control_state = meta_agent on success + room event

Before-After Behavior: Before: no Thread Control client exists — control can never be returned; one reply owns the room forever. After: a Return action calls release; on success the room reverts to MBA control with a timeline marker.
— Happy Path —
• AC-1: Given the app holds control, when the agent clicks "Return to Meta agent", then Thread Control release is called and on success the banner reverts within 3s (p95) and a "Returned" marker is appended.
• AC-2: Given control was returned, when the customer's next message arrives, then it arrives on standby and MBA answers (verified in beta with a live MBA number).

— Error / Unhappy Path —
• ERR-1: Given the Graph call fails, when the error returns, then control state stays app, an error toast with retry shows, and mba_release_control_failed is logged with Meta's error code.
• ERR-2: Given repeated failures (3 attempts), when the agent retries, then the toast suggests checking MBA status on the number and the failure count is visible in observability (§10 alert).

— Permission Model —
• CAN: agents with access to the room; supervisors
• CANNOT: read-only roles — action not rendered

— UI States —
• Loading: action shows pending state during the call
• Error: toast + unchanged banner
• Success: banner reverts + marker appended
[MBA-S06] — Feature flag + org rollout control

As a Qontak platform admin, I want the MBA-aware layer behind a per-org flag, so that rollout is controlled and reversible.
Should HaveData Fields:
mba_aware_inbox flag (per org, default OFF)

Before-After Behavior: Before: n/a — capability doesn't exist. After: flag OFF = events consumed and logged only (no UI, no persistence side effects beyond logging); flag ON = full S01–S05 behavior.
— Happy Path —
• AC-1: Given mba_aware_inbox is OFF for an org, when standby/handover events arrive, then they are logged for observability but no messages/room events are persisted and no UI elements render.
• AC-2: Given the flag flips ON, when the next event arrives, then persistence and UI behavior activate without deploy or migration.

— Error / Unhappy Path —
• ERR-1: Given the flag flips OFF after use, when agents open previously-persisted MBA rooms, then existing messages/markers remain visible (no data deletion) but new events stop persisting.
[MBA-S01-NEG] — No Qontak automation on MBA rooms (Guard Rail — from Non-Goal 5)

As Qontak's bot flow / AI agent, when a message arrives on a room MBA controls, then I do not respond.
Guard RailNEG-1: Given a room with control state meta_agent, when an inbound message arrives, then the Qontak bot flow / AI agent does not fire and no bot reply is sent (verified: zero double-responses in beta).
[MBA-S02-NEG] — No MBA configuration from Qontak (Guard Rail — from Non-Goals 1–2)

As an org admin, when I look for MBA settings in Qontak this phase, then no configuration surface exists.
Guard RailNEG-1: Given Phase 1 is live, when an admin opens the WhatsApp channel Account details page, then no Meta Business Agent section renders (Phase 2 scope) and no sync columns appear in Bot automation.

Dependencies: MBA-S03 depends on S01+S02; S04 depends on S02; S05 depends on S02+S04. MBA-S01/S02 have no intra-PRD dependencies.


9. Rollout

StageAudienceDurationExit condition
Stage 0 — Internal QAQontak test org on a Meta-allowlisted test WABA with MBA enabled (test consumer numbers via MBA allowlist)2 weeksAll §12 Gate 1 criteria pass on live MBA traffic
Stage 1 — Closed beta2–3 design-partner orgs with MBA pilot access, flag ON per org4 weeksGate 2 criteria pass; design-partner interviews validate the adoption-intent evidence gap (§CB)
Stage 2 — GAFlag default ON for all orgs with WhatsApp Cloud channelsGate 3

No data migration is involved — the feature only affects events arriving after the flag is ON; historical MBA conversations (from any pre-flag period) are not backfilled, and rooms with partial history render whatever was captured (transition behavior: the banner appears from the first post-flag handover event onward).


10. Observability

EventTriggerKey properties
mba_standby_message_persistedS7 #1/#2 successorg_id, room_id, sender_type, lag_ms
mba_standby_unroutableS7 #1 failurewaba_id, reason
mba_handover_eventS7 #4 consumedorg_id, room_id, new_controller, source_timestamp
mba_take_overS7 #5 optimistic fliporg_id, room_id, agent_id
mba_release_control_succeeded / mba_release_control_failedS7 #6org_id, room_id, agent_id, meta_error_code (failure)
mba_suppression_fired / mba_suppression_ambiguousS7 #7org_id, room_id

Dashboard owner: Chatbot squad (including the hub-core consumer widgets — squad owns the feature end-to-end).

Alert thresholds:

  • mba_release_control_failed rate > 5% over 1h → page Chatbot on-call.
  • Standby persistence lag p95 > 60s over 15m → page Chatbot on-call (consumer backlog).
  • Any mba_suppression_ambiguous > 10/day → investigate control-state coverage.

Post-Launch Monitoring Cadence

Weekly review of the dashboard for the first 4 weeks post-GA (owner: Dimas F + Chatbot EM). Trigger for immediate investigation: transcript completeness (< 99%) or any confirmed double-response (Qontak automation firing on an MBA-controlled room — target: zero).


11. Success Metrics

Adoption & Usage:

MetricDefinitionBaselineTarget
MBA rooms observed# rooms with ≥1 standby-persisted message, per org0 (capability doesn't exist)Tracking live for 100% of flag-ON orgs from Stage 1

Quality & Accuracy:

MetricDefinitionBaselineTarget
⭐ Transcript completenessMessages persisted ÷ messages delivered on standby+messages webhooks for MBA rooms0% (standby unprocessed)≥99% within 30 days of GA
Control-state accuracyRooms whose banner matches the latest messaging_handovers event (sampled audit)N/A100% in beta, sustained at GA
Double-response incidentsQontak automation replies on MBA-controlled roomsN/A0 — hard guard rail (MBA-S01-NEG)

Efficiency & Impact:

MetricDefinitionBaselineTarget
Hand-back usage% of taken-over MBA rooms returned via Thread Control releaseN/A — capability doesn't exist≥50% of taken-over rooms within 60 days of GA (validates the hand-back loop is real, not one-way)

12. Launch Plan & Stage Gates

GateStageCriteria (measurable)
Gate 1 — QA exitStage 0 → 1100% of test-matrix standby/handover payloads persisted correctly; Thread Control release verified round-trip on the live test number; zero cross-tenant events in a multi-org test; suppression verified (zero bot replies on MBA rooms)
Gate 2 — Beta exitStage 1 → 2Transcript completeness ≥99% across beta orgs; control-state accuracy 100% on sampled audit; mba_release_control_failed < 5%; no Sev-2+ incident attributable to the feature for 2 consecutive weeks
Gate 3 — GA healthPost-GA§10 monitoring cadence green for 4 weeks; flag remains the kill switch (flipping OFF reverts to logged-only behavior with no data loss)

13. Dependencies

#DependencyOwnerBlocking?Notes
1Chatbot squad BE capacity for the hub-core changes — webhook consumer cases, control-state model, Thread Control clientChatbot squad EMYESChatbot squad owns this end-to-end (AI Agent feature); the work lands in hub-core, so it is cross-repo but NOT cross-squad. Needs one committed BE engineer before the Epic moves to In Progress.
1ahub-core PR review by the repo's maintaining squadChat squad reviewersNOCode-review courtesy on their repo, not a delivery dependency.
2Meta MBA pilot/allowlist access on a Qontak-owned test WABADimas F via Meta partner managerYESGate 1 requires live MBA traffic. Mitigation while waiting: build against Meta's documented payloads + contract tests, so only Stage 0 verification (not development) blocks on access.
3Webhook field subscription — standby + messaging_handovers added to the app's subscribed fields in the Meta Developer PortalPlatform opsYESOne-time config; without it no events arrive at all.
4Figma frames for the four inbox elementsDesign (Wulan)NOLow-fi wireframes exist; FE can build from them, Figma follows for polish.
5Design-partner orgs with MBA pilot access for Stage 1Dimas F + CSNO (blocks Stage 1 timing, not build)2–3 orgs; also serves the declared evidence gap.

14. Key Decisions + Alternatives Rejected

14a — Decisions Made

DateDecisionRationale
2026-07-08Consume standby/messaging_handovers via the existing Kafka webhook consumer routing (new cases in the existing channel_event_type switch)Grounded in code: the consumer's event-type dispatch is the established pattern for every WhatsApp webhook field; no new service
2026-07-08Persist MBA echoes as first-class messages (not a shadow table)Inbox, search, retention, and future reporting all work for free; sender type distinguishes them
2026-07-08messaging_handovers is the source of truth for control state; agent-send flips state only optimisticallyMeta owns the handover; local guesses must reconcile to the webhook (S8 AC coverage)
2026-07-08Room-list indicator reuses the existing ChatList tag rendering with a new "Meta AI" value (corrected 2026-07-19: no tag prop exists — tags come from room.tags data; the indicator is a new prop or data-side injection)Zero new components in the room list; IA-alignment rule
2026-07-08Flag default OFF at beta, ON at GA, and the flag doubles as the kill switch (logged-only mode)Reversible rollout without deploys; observability keeps flowing even when OFF

14b — Alternatives Rejected

AlternativeWhy RejectedDate
Poll the Graph API for conversation state instead of consuming messaging_handoversPolling is rate-limited, laggy, and inverts Meta's push model; the webhook is authoritative2026-07-08
A separate "MBA inbox" view for observed conversationsSplits the agent's workflow; the whole point is one inbox with complete rooms2026-07-08
Defer standby persistence to Phase 2 and ship only control actions nowTake-over without a complete transcript is unusable; persistence IS the foundation2026-07-08
Block MBA-controlled rooms from agent replies until an explicit "Take over" clickAdds friction to escalation; Meta's model already treats send-as-takeover, we mirror it2026-07-08

15. Open Questions

#TypeQuestionOwnerDeadline
1AssumptionThe "Meta Business Agent" sender is modeled on the existing bot-participant mechanism (vs a new participant type) — final call in the RFCChatbot squad tech leadRFC review
2Open QuestionDo MBA replies count toward agent SLA / first-response metrics, or are MBA rooms excluded from SLA until takeover?Dimas F + Reporting PM2026-08-15
3Open QuestionPlacement of "Return to Meta agent" — composer inline action vs conversation-header action (wireframe flags both)Design (Wulan)Figma delivery
4RiskMeta's pilot-stage payloads may differ at GA — Mitigation: contract tests against documented payloads + Stage 0 verification on a live allowlisted number before any org-facing rolloutChatbot squadStage 0
5RiskTest WABA / MBA allowlist access is denied or slow — Mitigation: development proceeds on documented payloads; escalate via Meta partner manager; Stage 0 timeline (not build) absorbs the delayDimas F2026-08-01

PRD CHANGELOG

VersionDateBySectionTypeSummary
1.02026-07-08ClaudeAllCREATEDInitial Phase 1 PRD created from the MBA strategic-response grounding (hub-core + chatbot code grounding and IA-aligned wireframes, 2026-07-07).
1.12026-07-08Claude10, 13, 15MODIFIEDOwnership corrected per PM: Chatbot squad owns end-to-end including hub-core changes (cross-repo, not cross-squad); Chat squad reduced to PR review; observability + alert ownership and open-question owners updated.
1.22026-07-08ClaudeHeader, 6, 8MODIFIEDEmbedded the inbox control-states wireframe (wireframes/phase-1-inbox-control-states.svg) in §6; Header Figma line and story Mockup cells now link it.
1.32026-07-08Claude8MODIFIEDAdded the System-flow sequence diagram (mermaid) to §8, transcribing the numbered flow incl. both failure branches (per score-prd 14a.5).
1.42026-07-18ClaudeHeader, Scope, 5, 6, 7, 8, 10, 11MODIFIEDAPI-grounding vs Meta MBA docs v2.0.0 (§7 Meta API surface & build map): Thread Control hand-back verb corrected passrelease throughout (docs: only release functions; pass reserved), events renamed mba_release_control_*, client re-anchored to the new api.facebook.com MBA API client.
1.52026-07-18ClaudeHeader, 7MODIFIEDStandalone API-inventory doc dissolved into the Phase PRDs per PM instruction: §7 gains this phase's "Meta API surface & build map" table (Webhooks, Thread Control, MBA API client).
1.62026-07-19ClaudeHeader, Scope, 7, 8, 14MODIFIEDground-prd fixes: ChatList tag mechanism corrected (no tag prop — room.tags data; new prop or injection, RFC choice); webhook field subscription documented as app-level (neither subscribe call sends subscribed_fields); MBA client re-anchored on the system-user/BISU token pattern (not per-channel LOCKBOX); consumer full path named; suppression-point RFC choice recorded (hub-core fan-out serves all subscribers vs chatbot dispatch).
1.72026-07-19Claude8MODIFIEDMermaid parse fix: semicolons in sequence-diagram message text broke rendering (replaced with em dashes); diagram now validates with mmdc.