RFC Summary: Consistent Deal & Ticket Creation Experience — Phase 1: Bot & AI Creation Parity
Full RFC: see
docs/rfcs/consistent-deal-ticket-creation/bot-ai-creation-parity.mdStatus: IDEA (draft) | Type: backend / enhancement Author: Alma Syafira (PM) · rfc-starter (agent draft) | Target: 2026-Q3 PRD: Phase 1: Bot & AI Creation Parity Related repos: chatbot (producer BE) · hub-core / hub-chat (Omnichannel) · crm-fe-v3 (CRM FE)
Problem
Deals and tickets created by Chatbot or Agentic AI don't behave like manually-created ones: the Omnichannel infobar keeps showing the "Create Deal" CTA even after a record exists, the CRM record has no conversation contact or origin context, and there's no timeline entry showing how the deal originated. Agents lose trust in automation and waste time manually hunting for records they can't see.
What's Being Built
- CRM backend (qontak.com) persists a
creator_flag("bot"/"agentic_ai") on deals and tickets so the Creator field can read "Bot"/"AI". - CRM emits create + update + delete events to Omnichannel for bot/AI records that carry a chat-room id, so the infobar shows a live deal/ticket preview that stays fresh.
- CRM auto-associates the conversation contact at creation time (reusing existing logic).
- CRM auto-creates a chat-history timeline note (lightweight preview + room deeplink) on the deal/ticket detail.
- Chatbot BE (
chatbot) — the upstream producer — extends the create body it already sends to/api/v3.1/deals|ticketswithroom_id, contact, andcreator_flag. - Omnichannel (hub-core/hub-chat) renders the preview + new-tab navigation; crm-fe-v3 renders the timeline entry + creator label (UI embed pre-baked to crm-fe-v3).
Component Architecture
flowchart TB
subgraph clients[Actors]
AGENT[Sales/CS Agent browser]
end
subgraph bot[chatbot — Chatbot BE]
BFLOW[Flow / node executor<br/>mekari_qontak_crm/execute.rb]
BHTTP[CrmHttpClient<br/>company-token auth + 401 refresh]
end
AI[Agentic AI / Airene<br/>producer — repo TBD]
LB[(ALB / nginx ingress)]
subgraph crm[qontak.com — CRM BE - MAIN]
WEB[Rails web pods]
SK[Sidekiq workers]
REDIS[(Redis)]
DBP[(Postgres primary)]
DBR[(Postgres replica)]
end
subgraph hub[hub-core — Omnichannel BE]
HWEB[Rails web pods]
HDB[(Postgres: qontak_crm_objects / room_tickets)]
ES[(Elasticsearch)]
end
subgraph fe[Frontends]
HUBFE[hub-chat]
CRMFE[crm-fe-v3]
end
BFLOW --> BHTTP -->|POST /api/v3.1/deals,/tickets| LB
AI -.->|POST create| LB
LB --> WEB
WEB --> DBP
WEB --> SK
SK -->|webhook + chat history| HWEB
HWEB --> HDB
HWEB --> ES
AGENT --> HUBFE -->|read preview| HWEB
AGENT --> CRMFE -->|read timeline/creator| WEB
WEB -->|reads| DBR
Repo Map (files touched)
flowchart LR
subgraph CB[BE: chatbot READ-ONLY]
CEX[execute.rb<br/>add room_id/contact/creator_flag]
CHC[crm_http_client.rb<br/>company-token]
end
subgraph CRM[BE: qontak.com MAIN]
DS[crm/deals/create_service.rb<br/>modified]
TC[ticket/create.rb<br/>modified]
PB[webhook entities<br/>modified]
NW[timeline-note worker<br/>new]
MIG[migrate creator_flag<br/>new]
end
subgraph HUB[BE: hub-core READ-ONLY]
DR[deal_receiver.rb<br/>add update]
TR[ticket_receiver.rb<br/>add create/update]
end
subgraph FE[FE READ-ONLY]
HD[hub-chat preview]
CT[crm-fe-v3 timeline]
end
CEX --> CHC
CHC -.POST /api/v3.1/deals.-> DS
DS --> MIG
DS --> NW
DS --> PB
PB -.webhook.-> DR
PB -.webhook.-> TR
DR --> HD
NW --> CT
Infobar Preview State Machine
stateDiagram-v2
[*] --> CTA: no record
CTA --> Preview: create event received
Preview --> Preview: update event (latest fields) / newer record (latest-wins)
Preview --> CTA: delete event
Preview --> Error: hub cannot reach CRM (CDTC-S01 ERR-2)
Error --> Preview: retry/reload
Sequence Diagrams
Happy path — bot/AI creates deal → preview + timeline
sequenceDiagram
participant BOT as chatbot BE (execute.rb)
participant LB
participant WEB as CRM web
participant DBP as PG primary
participant SK as Sidekiq
participant HUB as hub-core
BOT->>BOT: build body (room_id, contact, creator_flag)
BOT->>LB: POST /api/v3.1/deals (company token)
LB->>WEB: route
WEB->>DBP: INSERT deal (room_id, creator_flag) + contact assoc
WEB-->>BOT: 201 Created
WEB->>SK: enqueue SendWebhookWorker + TimelineNoteWorker
SK->>HUB: POST qontak.crm.deal.create
HUB->>HUB: upsert QontakCrmObject (latest-wins)
SK->>HUB: GET chat history (room_id)
SK->>DBP: INSERT Crm::Note (preview + deeplink)
Failure path — chatbot CRM create token expired (producer side)
sequenceDiagram
participant BOT as chatbot CrmHttpClient
participant WEB as CRM web
BOT->>WEB: POST /api/v3.1/deals (Bearer access_token)
WEB-->>BOT: 401 Unauthorized
Note over BOT: request_with_auth retries once
BOT->>WEB: POST /api/v3.1/teams/company_token (sso_id + X-Crm-Api-Key)
WEB-->>BOT: new token
BOT->>WEB: POST /api/v3.1/deals (new token)
Note over BOT: on refresh fail → no deal created; Rollbar
Key Technical Decisions
| # | Decision | Chosen | Rationale |
|---|---|---|---|
| 1 | CRM UI embed | crm-fe-v3 | pre-baked by author; timeline component already exists there |
| 2 | CRM→Omnichannel transport | Reuse existing webhook pipeline | create/delete already work end-to-end; "new API" = additive event actions |
| 3 | Preview freshness | Emit update events too | PRD AC-4 requires latest Name/Stage/Status (conditional on OQ-1) |
| 4 | Bot/AI actor storage | New nullable creator_flag column | creator_id is a User FK; data_source already means transport |
| 5 | Timeline entry | Lightweight Crm::Note + deeplink, async | reuse existing note types; no new schema; PRD rejected full transcript |
| 6 | Multiple records per room | Latest-wins by CRM created_at | deterministic vs non-deterministic receipt time |
| 7 | Chatbot producer | Extend existing CRM node executor body | executor + company-token auth + retry already exist; add 3 fields |
APIs at a Glance
| Method | Endpoint | Status | Owner |
|---|---|---|---|
POST | /api/v3.1/deals (add room_id/contact/creator_flag) | extend body | Chatbot Squad (chatbot) |
POST | /api/v3.1/tickets (add room_id/creator_flag) | extend body | Chatbot Squad (chatbot) |
POST | qontak.crm.deal.create webhook | exists (reused) | CRM (qontak.com) |
POST | qontak.crm.deal.update webhook | needs receiver branch | CRM emit / hub-core receive |
POST | ticket create / update webhook | needs-building | CRM + hub-core |
GET | /api/open/v1/messages/rooms/:id (chat history) | exists (reused) | hub-core |
Open Blockers
| # | Blocker | Owner | Blocks |
|---|---|---|---|
| OQ-1 | How hub-chat sources deal Name/Stage/Pipeline (live fetch vs stored) | Omnichannel Eng | update-sync design |
| OQ-2 | No sender/receiver path for bot/AI ticket create+update today | CRM + Omnichannel Eng | ticket preview |
| OQ-4 | Exact hub-chat room deeplink URL pattern (none found in repo) | Omnichannel Eng | timeline → room nav |
| OQ-10 | Contact contract mismatch: chatbot sends crm_lead_ids, PRD/CRM uses contact_id | Chatbot + CRM Eng | contact auto-association |
| — | InfoSec approver + feature flag name + discussion link required | CRM Eng / PM | §7 sign-off |
Rollout
| Stage | Audience | Go/No-go evidence |
|---|---|---|
| Internal Alpha | Qontak internal accounts (Chatbot + AI + CRM + Omni) | all deps integrated; bidirectional nav works; timeline log for ≥95% creates |
| Closed Beta | 5–10 CSM-nominated customers | preview success ≥95%; no critical defects; no room-load latency regression |
| GA | All Chatbot/Agentic AI plan accounts | beta gates sustained ≥2 weeks; PMM approval |
Stop conditions: deal_preview_displayed drops to 0 while creates >0 for 15 min; timeline_log_creation_failed > 5%/1h; P95 room load > baseline + 200ms.
Rollback: toggle the per-account feature flag OFF → bot/AI events stop emitting; creator_flag column is inert when unread (no down-migration needed); confirm preview rate + P95 latency recover; revert PR only if necessary.
Generated from RFC: Consistent Deal & Ticket Creation Experience — Phase 1: Bot & AI Creation Parity · 2026-06-30