Skip to main content
The gb-agent connector is a Switchbord integration that lets an external customer-agent service (GBCA) propose replies for inbound conversations. The connector posts a structured response into Switchbord, the worker persists it on the message draft row, and the inbox renders it as a card the operator can accept, edit, dismiss, or review — depending on the agent’s verdict. This page is operator-facing. It explains what the connector does, how the inbox surfaces it, and — most importantly — what the connector cannot do.

Two card variants

Every connector response carries a route field. The inbox renders one of two card variants based on that route.

draft_reply — proposed message

The agent is confident enough to suggest a ready-to-send reply. The operator sees the existing AI-draft card:
  • Header: ✨ AI draft · proposed time
  • Metadata badges: route, intent, confidence, optional destination/dateWindow/partySize
  • Body: the proposed message text
  • Actions: Accept & send, Edit, Reject
This card is unchanged from earlier Switchbord releases.

human_review — handoff preview

The agent does not have enough confidence (or the policy disallows autosend) and is handing the conversation back to a human. The operator sees the new amber handoff preview card:
  • Header: 🧭 Agent handoff preview · proposed time
  • Badges: route (Human review), connector (gb-customer-agent), optional handoff type
  • Sections (each only shown when populated):
    • Handoff packet — type, recommended queue, open tasks, required verification, dry-run boundary
    • Proposed review actions — review-only chips (no click handlers, no side effects); each labelled “Review-only”
    • Evidence references — up to 5 visible, https-only links, mono-style IDs for non-link evidence
  • Actions: Dismiss only (no Accept/Send, no create-operator-task, no queue-assignment button)
The card is read-only by design. Everything the operator does next happens through the standard Switchbord inbox controls — assign queue, change status, send a manual reply, escalate. The connector never performs any of those actions on the operator’s behalf.

Feature flag

The handoff preview card is gated by the inbox.gbAgentHandoffPreview feature flag.
  • Default: OFF in all workspaces.
  • Enable per-deployment: set the env var NEXT_PUBLIC_FLAG_INBOX_GB_AGENT_HANDOFF_PREVIEW=1 on the Vercel apps/app project. Flag flips next deploy.
  • When OFF: drafts with route === "human_review" continue to render the thin “Human review” badge inside the existing draft card. Nothing else changes.
  • When ON: Variant B renders for those drafts.
See Operations → Feature Flags for the broader flag catalogue.

The dry-run boundary

By default, the gb-agent connector is restricted to proposing — not acting. This is enforced in three layers (UI, worker, and the response schema itself) and is the contract you can rely on for manual Draft with me, manually queued connector drafts, human_review handoffs, and proactive draft mode when proactive_draft_auto_send_first=false: The only intentional exception is the explicit workspace opt-in documented below: proactive_draft_auto_send_first=true may accept and dispatch the first successful Flow/keyword-triggered proactive draft_reply through the same accept_message_draft_for_dispatch RPC an operator uses. Outside that narrow path, effects remain manual operator actions inside Switchbord. If you see any other autonomous inbox action as a result of a connector response, that is a bug — please file it.

”Draft with me” (per-conversation streaming assist)

Separately from the two card variants above, the composer has a “Draft with me” trigger — a single violet pill (SparklesIcon) on the composer’s right edge, gated behind the same draft_with_me access mode as the rest of the connector surface (/api/inbox/gb-agent/access). Clicking it streams a full-context draft for the open conversation and stages it in a minimized status bar above the composer; nothing is ever auto-sent.
As of v0.18.29, Draft with me is the only AI trigger in the composer — the older inline BotMessageSquare run-action and its compact duplicate were retired so operators have a single, unambiguous entry point.

Per-conversation, background runs

Since v0.18.30, a Draft with me run is keyed to its conversation, not to the composer’s mounted component. Run state (status, phase, trace, final draft text, handoff reason) lives in a module-level store (gb-agent-draft-runs-store) indexed by conversation id. Practically, this means:
  • Switching to a different chat while a draft is generating does not cancel it — the run keeps streaming in the background and is still there (finished, handed off, or failed) when the operator switches back.
  • Every conversation row, the open chat’s header, and the tab strip (see Advanced Inbox Features) show a live BotMessageSquare status badge tinted by that conversation’s own run — violet/pulsing while streaming, green when a draft is ready, amber on a human-review handoff, red on failure, and hidden when idle.
  • Only the explicit ✕ / Stop control cancels a run. Unmounting the composer (by navigating away) no longer aborts it.
  • Starting a new run for the same conversation supersedes whatever was running before.

Minimized status bar

The panel (v0.18.17) never covers the chat — it renders as a thin bar with a color-coded status dot and a click-to-expand disclosure: Expanding the bar reveals the streaming answer text, a collapsible reasoning/tool trace, and (while active) a Stop control plus the always-visible ✕ that dismisses the bar entirely (cancelling a still-streaming run). panelDismissed and panelExpanded both reset on conversation switch and on every new run, so a stale bar never bleeds across chats. If a new inbound message arrives mid-run, a dismissible amber “stale context” banner warns the operator the in-flight draft may be answering an outdated message — the run is not cancelled automatically.

Fallback chain

Since v0.18.20, a Draft with me run is a chain of attempts, not a single request: stream → jittered-backoff retry of the stream → deterministic non-stream draft-run fallback. The operator always ends with either a staged, usable draft or an explicit, friendly failure — never a silently empty composer. While the chain is working through retries or the deterministic fallback, the expanded bar shows a “retrying…” / “falling back…” hint so the wait is explained rather than silent.

Copy chat → draft debug trail

The chat pane’s Copy chat button (top-right, clipboard icon) writes a structured JSON export to the clipboard containing the full conversation context (v0.18.13). Since v0.18.33, if a Draft with me run has ever executed for that conversation, the export also carries a draftWithMe block — omitted entirely when no run has happened (status is idle), so conversations that were never drafted for stay clean:
  • finalDraftText and lifecycle (status / phase, whether the deterministic fallback was used)
  • handoffReason when the run routed to human review
  • traceEvents (labels, decision/event type, ok/warn status) — the same reasoning trace shown in the expanded bar
  • sessionId and inboundMessageId for correlating with server-side logs
  • lastError (message, recoverable flag, and — since v0.18.34 — a content-free upstream detail such as AI_APICallError status=429 cause=RateLimitError, bounded to 200 characters) when the run’s terminal state was a failure
This lets an operator grab a draft plus everything needed to triage an issue without opening devtools.

Proactive campaign Flow drafts and auto-send-first

Independent of the manual “Draft with me” trigger, a workspace can opt in to proactive draft generation (v0.18.12, BORD-818): GB-Agent generates a draft in the background and stages it in the inbox composer before the operator opens the conversation. For GB campaign Flow forms, the production WhatsApp shape is:
  • Meta webhook: type: "interactive", interactive.type: "nfm_reply", interactive.nfm_reply.name: "flow".
  • Customer-visible/message body placeholder: "Sent".
  • Switchbord normalized message: body = "Sent", payload.messageType = "interactive", payload.replyKind = "interactive_flow", and payload.flowResponseRedacted present.
  • Campaign-origin conversations usually carry a campaign/Campaign label, but the Flow trigger itself should key on payload.replyKind = "interactive_flow"; the sent keyword is a fallback for degraded payloads, not the primary proof.
Recommended toggle sets: Operational notes:
  • proactive_draft_delay_seconds (0–300, default 10) waits after the inbound message before generation. A follow-up inbound message cancels a still-pending proactive job and restarts the delay against the newest message.
  • proactive_draft_label_filters is case-insensitive; campaign matches labels such as Campaign. Leave it empty to allow any Flow completion, including Meta ad/organic Flow completions.
  • proactive_draft_auto_send_first is deliberately narrow: only the first flow_completion/keyword proactive draft_reply in a conversation may be accepted automatically. Legacy all_inbound jobs, human_review drafts, conversations with a prior accepted draft, conversations with a human-authored outbound message, and any RPC send block degrade to draft-only.
  • Auto-send still uses the same accept_message_draft_for_dispatch RPC as the operator Send button. That preserves idempotency, audit, consent/service-window guards, and the normal message.dispatch outbox path.
Keep proactive_draft_auto_send_first off for draft-in-composer review mode. Enable it only when the workspace explicitly wants the first successful Flow-response draft sent automatically; subsequent replies remain draft-only.

GB-Agent Insights

/insights has a GB-Agent tab (v0.18.16, BORD-741) — read-only, workspace-scoped rollout and refinement analytics over responder_runs (the same telemetry table Draft with me and proactive drafts write to). There is no cost-control gate here; the tab exists purely to make GB-Agent usage visible. The tab surfaces:
  • Summary tiles — requests, completions, timeouts, failures, plus completion/timeout/failure rates.
  • Outcome breakdownsent_unmodified, sent_modified, rejected, abandoned, timeout, failed, with the derived modification rate (sent_modified / (sent_unmodified + sent_modified)) and rejection rate.
  • Edit-distance distribution — a bucketed histogram (0–10%, 10–25%, 25–50%, 50–75%, 75%+) of how much of a sent_modified draft’s text an operator changed before sending, plus the average ratio.
  • Time-to-send percentiles — p50 / p90 / p99 / average operator decision latency (time_to_action_ms).
  • By mode and by operator/agent rollups — the same aggregate breakdown grouped by responder_type (Response AI vs. campaign) or by agent_id.
The tab reads GET /api/insights/gb-agent, which accepts sinceDays (default 30, clamped 1–365) or explicit from/to and shares the SSR channel/date-range selectors with the rest of /insights. Access is gated the same way as the rest of /insights (requireOperationsAccess).

Configuring the connector

Operator setup is done in Settings → Integrations → gb-agent. (When this surface ships in full, in-product help will cover the steps in detail; this page is the current source of truth.) Required configuration:
  1. Endpoint URL — the full draft-run endpoint where Switchbord posts inbound-message context for evaluation. For GBCA production, use https://gb-customer-agent-production.up.railway.app/api/agent/draft-run, not just the service root. Must be https with no userinfo, query string, or fragment.
  2. Tool-surface token — stored as a workspace secret in Supabase Vault, never returned in any API response. Rotate via the Vault UI.
  3. Test connection — the Settings page performs a safe authenticated GET to the configured endpoint and expects the connector to report a draft_run capability before showing success.

Triage states

When the inbox trigger is disabled, the badge tells you why:

See also