Skip to main content

[PRD] Supporting Create Ticket from CDP Mobile (Native + Auto-Associate)

HEADER BLOCK

FieldValue
PMZhelia Alifa
PRD Version1.1
StatusDRAFT
PRD TypeSUPPORT
EpicTBD — add once Epic is created
SquadCDP Squad × Mobile/CRM Squad
RFC LinkN/A — reuses the native Mobile Ticket Module (no new RFC)
Figma MasterMobile CRM — Tickets
AnchorYes — Create Ticket & Auto-Associate from CDP (Cross-Platform)
Labelsepic:qontak-cdp | module:customers | feature:create-ticket-from-cdp-mobile
Last Updated2026-06-26

This is a SUPPORT PRD. The native ticket-create screen is owned by the Mobile Ticket Module (TF-3053 / Ticket Module in Mobile App PRD). This PRD covers the CDP-mobile contribution — adding a "Create new ticket" entry point to the contact detail, routing into that native create screen, and auto-associating the result back to the contact. CDP mobile today is associate-existing only; this closes that gap.


2. SUPPORT Context

FieldDetail
Anchor PRDCreate Ticket & Auto-Associate from CDP (Cross-Platform) — this is the mobile track.
Lead-squad componentThe native ticket-create screen CreateTicketScreen + create API POST /v2.8/tickets, owned by the Mobile Ticket Module (TF-3053). Note: TF-3053 §[26Q2] Ticket Association with CDP Mobile AC1.4 already describes "Create new ticket → native create page → auto-associate" — this PRD is the CDP-side detail/contract for that AC.
This squad's contribution (CDP-mobile)In mobile-qontak-crm → contact detail "Others" tab: add a "From Existing / Create New" ticket bottom sheet (the "Create New" branch is the net-new part); route to CreateTicketScreen; on return, auto-associate the created ticket to the contact; refresh the association summary; pre-fill the customer in the create form.
Integration point (contract)(1) Navigate navigationHelper.pushNamed(QontakAppRoute.createTicket, CreateTicketArgument(pipelineId)).then((result) => …); (2) on a non-null created ticket, call the existing associateTicket()PUT /v2.8/tickets/{ticketId} with qontak_customer_ids = contactId. Both already exist in the repo — this PRD wires them together.
Handoff contract + dateMobile Ticket Module delivers: the native CreateTicketScreen returning the created ticket on pop (already shipped). CDP-mobile delivers: the contact entry point + auto-association. Date: TBD — mobile native create already exists, so this track is NOT blocked by the CRM embed (unlike the web track).
Graceful degradationIf "Create New" is unavailable (e.g. flag off / no ticket-create access), the ticket bottom sheet degrades to associate-existing only — exactly today's behavior. Create is purely additive.

3. One-liner + Problem

One-liner: Let a CDP-mobile user create a new ticket from a contact (via the existing native ticket form) and have it auto-associated to that contact — not just associate an existing one.

Problem: In mobile-qontak-crm, the contact detail "Others" tab ticket section (other_tab.dart:529–560) only offers "associate existing" (_onAddTicket()TicketAssociationListBottomSheet). There is no "Create new ticket" option for tickets, even though that exact "From Existing / Create New" pattern already exists for companies, deals, and products. So a field/mobile agent who needs to raise a ticket on a customer must leave the contact, open the Ticket module, create the ticket, then come back and associate it manually — daily, high-frequency friction.


4. Target Users + Persona Context

PersonaRoleGoalPainWorkaround
Primary — Field / Mobile CS AgentAgent using mobile-qontak-crm on the moveRaise + link a ticket from the contact on a phoneNo "Create new ticket" from a contact — associate-existing onlyLeaves the contact → Ticket module → creates → returns → associates manually
Secondary — CS Supervisor (mobile)Reviews a customer's tickets on mobileCapture a new issue in-context during a customer interactionSame gap; loses the customer context mid-taskSame leave-and-return workaround

5. Non-Goals

  1. Not building the native ticket formCreateTicketScreen, pipeline selection, layout/custom fields are owned by the Mobile Ticket Module (TF-3053). This PRD only routes into it and handles the return.
  2. Not changing the create APIPOST /v2.8/tickets is unchanged.
  3. Not changing associate-existing — the existing TicketAssociationListBottomSheet flow stays.
  4. No web scope — web is the sibling SUPPORT PRD (embed iframe).
  5. No ticket edit/delete from the contact — out of scope (existing module behavior).
  6. No offline create — requires network like the rest of the ticket module.
  7. No new permission system — reuses mobile's existing ticket feature flag + contact permission.update.

Scope Changes

Engineering surfaces this PRD touches (controlled vocab: Backend · Frontend · Mobile · Infra · Data · Design · Docs · None).

  • Mobilemobile-qontak-crm: in features/crm_contact/.../other_tab.dart, replace the ticket section's direct _selectExistingTickets() call with the shared AssociationBottomSheet ("From Existing" / "Create New" — same util used for companies/deals/products); on "Create New", pushNamed(QontakAppRoute.createTicket, CreateTicketArgument(pipelineId)) and on a non-null return call associateTicket() (PUT /v2.8/tickets/{id}, qontak_customer_ids = contactId); pre-fill the customer/contact in the create form; refresh the association summary; gate "Create New" on the ticket feature flag + permission.update.
  • Design — Figma for the ticket "Create New / From Existing" bottom sheet + the post-create success/refresh state on the contact detail (align to the existing deal/product pattern).
  • BackendNone (create + associate endpoints already exist; the native create keeps its existing data_source: "Mobile Flutter" — no embed_source on the native path; see MD-5).

6. Constraints

ConstraintValue
PlatformMobile only (mobile-qontak-crm, Flutter). Web is the sibling PRD.
Component IDCP-QONTAKCRM-2025-0002 (ticket features shown per the user's active package — per the Mobile Ticket Module PRD).
Feature gatingMobile uses feature flags (isTicketEnabled via GetProfileBloc) + contact-level permission.update + !isTSOnot the granular web keys (tickets_general_manage, etc.). The "Create New" entry inherits this gating.
Create APIPOST /v2.8/tickets with TicketRequestModel { id, layout_id, data_source: "Mobile Flutter", crm_properties[] } (existing).
Associate APIPUT /v2.8/tickets/{ticketId} with a ContactResponse carrying crm property qontak_customer_ids = contactId (UUID for C360 / numeric for CRM) (existing — contact_360_remote_data_source.dart:296).
Layout/custom fieldsLoaded by the native form via feature flag ticketLayoutEnabled + GetTicketCRMProperties(layoutId, pipelineId) — no CDP-mobile logic.
C360 vs CRM contactC360 (CDP) contact uses id360 (UUID) and Contact360AssociationBloc; CRM contact uses numeric id. Auto-association must use the right identity per mode.
Backward compatAssociate-existing flow unchanged; "Create New" is additive + gated.

7. New Features

7.1 "Create New" branch in the contact ticket bottom sheet

ElementBeforeAfter
Ticket section in contact "Others" tab (other_tab.dart:529–560)_onAddTicket()_selectExistingTickets()TicketAssociationListBottomSheet (existing only)_onAddTicket() → shared AssociationBottomSheet with "From Existing" + "Create New" (same pattern as companies/deals/products)
"Create New" actionDoes not exist for ticketspushNamed(QontakAppRoute.createTicket, CreateTicketArgument(pipelineId: defaultPipelineId)); await the popped result
On created ticketn/aCall associateTicket() (PUT /v2.8/tickets/{id}, qontak_customer_ids = contactId), then refresh the association summary + show success
Customer pre-filln/aPre-fill the Customer/Contact crm-property in the create form from the contact (C360: qontak_customer_ids = id360)

Reuse map (grounded):

  • Bottom sheet: QontakAssociationFormUtils.openAssociationBottomSheet(...) (association_bottom_sheet.dart) — already returns true (existing) / false (create new) for other modules.
  • Create screen: CreateTicketScreen + route QontakAppRoute.createTicket (route_generator.dart:2590).
  • Associate: AssociateTicketUseCase / Contact360AssociationBloc.associateTicket() (contact360_association_bloc.dart:142).

4 UI States (Create-New branch):

  • Loading: native create screen handles its own loading; on return, a brief associating spinner on the contact.
  • Empty: N/A.
  • Error: create fails inside the native screen (native handles it) → user returns with no ticket, nothing associated; associate failure on return → inline error + retry (ticket still exists).
  • Success: ticket created + associated → association summary refreshes + success toast/snackbar.

8. API Behavior

CDP-mobile adds no new API. It composes two existing calls. Contract below.

#BehaviorEntityTriggered ByExpected BehaviorFailure Behavior
1Open create from contactUser taps Tickets "+" → "Create New"pushNamed(QontakAppRoute.createTicket, CreateTicketArgument(pipelineId)); native form opens with the customer pre-filledIf no default pipeline resolvable → native form shows pipeline selection (its own behavior)
2Create the ticket (native)TicketUser submits the native formPOST /v2.8/tickets { data_source:"Mobile Flutter", layout_id, crm_properties[] }; returns the created ticket; screen pops with the resultNative validation/network error → handled in the native screen; user returns with no created ticket
3Auto-associate on returnContact↔TicketCreate screen pops with a non-null ticketPUT /v2.8/tickets/{ticketId} with qontak_customer_ids = contactId (C360: id360); refresh association summaryAssociate fails → cdp_mobile_ticket_associate_failed logged; inline error + retry; ticket still exists in CRM
4Degrade to existingFlag off / no create access"Create New" hidden; only "From Existing" shown

System Flow — CDP Mobile Create + Auto-Associate

Contact Detail (Others tab) → tap Tickets "+"


AssociationBottomSheet ──"From Existing"──► TicketAssociationListBottomSheet (unchanged)

└──"Create New"──► pushNamed(createTicket, pipelineId) [customer pre-filled]


CreateTicketScreen → POST /v2.8/tickets
│ pop(createdTicket)

createdTicket != null ? ──yes──► associateTicket(): PUT /v2.8/tickets/{id}
│ (qontak_customer_ids = contactId)
│ │
│ success ► refresh summary + toast
│ fail ► log + inline retry
└──no──► return, nothing associated

9. System Flow + User Stories + ACs

9.1 System Flow

  1. Agent opens a CDP contact → Detail → Others tab → Tickets row (shows count + "+").
  2. Tap "+" → AssociationBottomSheet with From Existing / Create New (Create New gated on ticket flag + permission.update).
  3. Create New → navigate to native CreateTicketScreen (default pipeline; customer pre-filled).
  4. Agent fills + submits → native POST /v2.8/tickets → screen pops returning the created ticket.
  5. On non-null return → CDP-mobile calls associateTicket() (PUT /v2.8/tickets/{id}, qontak_customer_ids = contactId) → refresh the association summary + success toast.
  6. Failure branches: create cancelled/failed → return with nothing; associate failed → inline retry, ticket still exists.

9.2 User Stories

User StoryImportanceMockupTechnical NotesAcceptance Criteria
[MTCK-S01] — "Create New" option in the contact ticket sheet

As a mobile CS agent, I want a "Create new ticket" option from a contact, so I can raise a ticket in-context.
Must HaveMobile CRM — Tickets (Figma) — align to the deal/product bottom sheetBefore-After: Before — _onAddTicket()_selectExistingTickets() only (other_tab.dart:1372). After — _onAddTicket() → shared AssociationBottomSheet (openAssociationBottomSheet) with "From Existing"/"Create New", same as companies/deals/products.— Happy Path —
• AC-1: Given the ticket feature is enabled and the user has permission.update, when they tap the Tickets "+", then a bottom sheet shows From Existing and Create New.
• AC-2: Given they tap "Create New", then the native CreateTicketScreen opens.
— Error / Unhappy Path —
• ERR-1: Given the create screen can't resolve a default pipeline, then the native pipeline selection shows (native behavior).
— Permission Model —
• CAN: ticket-enabled + permission.update + !isTSO.
• CANNOT: otherwise → only "From Existing" shows (NEG-1).
— UI States —
• Loading/Empty/Error/Success per §7.1.
[MTCK-S02] — Pre-fill the customer in the native form

As a mobile CS agent, I want the contact pre-filled in the new ticket, so I don't re-enter it.
Must HaveFigma — create form with Customer pre-filledBefore-After: Before — create opened standalone with no customer. After — pass the contact so the customer crm-property is pre-filled. C360: qontak_customer_ids = id360; CRM: numeric id (mirrors associateTicket() payload).— Happy Path —
• AC-1: Given a C360 contact, when the create form opens from it, then the Customer field is pre-filled from id360 (editable).
• AC-2: Given a CRM contact, then it pre-fills from the numeric contact id.
— Error / Unhappy Path —
• ERR-1: Given the pre-fill can't resolve, then the form opens with the Customer empty/editable; submit still allowed.
[MTCK-S03] — Auto-associate the created ticket on return

As a mobile CS agent, I want the new ticket linked to the contact automatically, so it shows under the contact.
Must HaveFigma — success state + refreshed association summaryBefore-After: Before — n/a. After — on CreateTicketScreen pop with a non-null ticket, call associateTicket()PUT /v2.8/tickets/{ticketId} with qontak_customer_ids = contactId via Contact360AssociationBloc (contact360_association_bloc.dart:142); refresh summary (other_tab.dart:1416 pattern).— Happy Path —
• AC-1: Given the create screen pops with a created ticket, when CDP-mobile receives it, then it calls associateTicket() and the contact's ticket count + list update.
• AC-2: Given association succeeds, then a success toast shows and the new ticket appears under the contact.
— Error / Unhappy Path —
• ERR-1: Given the associate call fails, then cdp_mobile_ticket_associate_failed is logged, an inline retry is offered, and the ticket still exists in CRM (not lost).
• ERR-2: Given the user cancels/creation fails, then the screen returns with nothing and no association is made.
[MTCK-S04] — Pipeline + layout inherited from the native module

As a mobile CS agent, I want correct pipeline + fields in the new ticket.
Should HaveFigma — pipeline/layout (native)Before-After: Inherited from the Mobile Ticket Module — TicketPipelineBloc (pipeline) + GetTicketCRMProperties(layoutId, pipelineId) + ticketLayoutEnabled. No CDP-mobile logic.— Happy Path —
• AC-1: Given one pipeline, then the form uses the default pipeline's layout.
• AC-2: Given multiple pipelines, then pipeline selection shows first (native). (Owned by Mobile Ticket Module.)
[MTCK-S05-NEG] — No create without access (Guard Rail)

As the system, block create-from-contact when the user lacks ticket access.
Guard Rail• NEG-1: Given the ticket feature flag is off OR permission.update is false OR isTSO, then "Create New" is not shown — only "From Existing" remains.
• NEG-2: Given no ticket view access at all, then the Tickets entry follows existing hiding behavior.

Test Coverage Matrix — [MTCK-S03]

DimensionCoverageNotes
Boundary values⚠️ partial⚠️ QA: create succeeds but app backgrounded before associate returns
State transitions✅ definedcreated → associate → refresh; cancel → no-op
Data validation✅ definedcorrect identity per mode (C360 id360 vs CRM numeric)
Concurrency⚠️ TBD⚠️ QA: rapid double-return / duplicate associate
Network/timeout✅ definedERR-1 associate failure + retry

10. Rollout

FieldDetail
FlagReuse the mobile ticket feature flag (isTicketEnabled); optionally a CDP-mobile "create from contact" sub-flag for staged rollout.
Stage 1 — Internal QAEnable on a QA build; verify bottom sheet, pre-fill (C360 + CRM), auto-associate, refresh, degradation.
Stage 2 — Closed BetaA few companies; monitor create + associate success.
Stage 3 — GAProgressive enable.
Backward compatAssociate-existing unchanged; Create New additive + gated.
Dependency gateRequires the Mobile Ticket Module native create (TF-3053) shipped — not blocked by the CRM embed (that blocks only the web track).

11. Observability

Event NameTriggerProperties
cdp_mobile_ticket_create_opened"Create New" tapped from a contactcompany_sso_id, contact_id, is_c360, user_id
cdp_mobile_ticket_createdNative create returns a ticketcontact_id, ticket_id, pipeline_id
cdp_mobile_ticket_associatedAuto-associate succeedscontact_id, ticket_id
cdp_mobile_ticket_associate_failedAuto-associate fails on returncontact_id, ticket_id, reason

Dashboard owner: CDP Squad (Mixpanel — the Mobile Ticket Module PRD already calls for a create-ticket tracker). Alert: associate-failed > 5% → investigate.


12. Success Metrics

MetricDefinitionBaselineTarget
⭐ Create-from-contact adoption (mobile)% of mobile tickets on CDP contacts created via this flow within 30 days of GA0 (net-new)≥ X% (set at beta)
⭐ Auto-association successassociated / created-from-contactN/A≥ 99%
ParityCreate-from-contact available wherever associate-existing is, on mobileAssociate-only today100% of mobile contact-detail surfaces

13. Dependencies

DependencyOwning TeamDeliverable NeededBlocking?
Native ticket create screenMobile Ticket Module (TF-3053)CreateTicketScreen returning the created ticket on pop (shipped)YES (already met)
Associate APICRM BackendPUT /v2.8/tickets/{id} with qontak_customer_ids (exists)YES (already met)
Shared association bottom sheetMobile/CRMAssociationBottomSheet "From Existing/Create New" util (exists for other modules)YES (already met)
CDP-mobile integrationCDP × Mobile SquadWire entry point + pre-fill + auto-associate + refresh in other_tab.dartYES (this PRD's scope)
Customer pre-fill into native formMobile Ticket ModuleAccept a pre-filled customer crm-property argument on CreateTicketScreenConfirm — small extension (OQ-M2)

14. Key Decisions + Alternatives Rejected

14a — Decisions Made

IDDecisionRationale (grounded)
MD-1Reuse the native CreateTicketScreen, not a webview/embed.Native screen already exists; mirrors AD-3 in the anchor. Best mobile UX/perf.
MD-2Extend the existing "From Existing / Create New" bottom-sheet pattern to tickets.The util (openAssociationBottomSheet) is already used for companies/deals/products; tickets are the missing module.
MD-3Auto-associate on return via the existing associateTicket() (PUT /v2.8/tickets/{id}, qontak_customer_ids).Reuses the proven association path + identity handling (C360 id360 vs CRM numeric).
MD-4Reuse mobile's native gating (feature flag + permission.update).Mobile doesn't use the granular web permission keys; consistency with the rest of the app.
MD-5Keep the native data_source: "Mobile Flutter"; no embed_source on the native path.Mobile creates via the native CreateTicketScreen + native associate flow (not the CRM web embed), so the web embed_source allow-list mechanism does not apply. CDP-origin labeling is a web-track concern only.

14b — Alternatives Rejected

AlternativeWhy Rejected
Embed the CRM web ticket form in a mobile webviewRegresses UX/perf; duplicates the web approach where a native one already exists.
A new bespoke create-from-contact flowThe shared bottom-sheet + native create + associate already exist; bespoke = needless duplication.
Fold this into the Mobile Ticket Module PRDKeeps CDP-contribution ownership separate from the native-form ownership (anchor AD-5); this is the SUPPORT side of TF-3053 AC1.4.

15. Open Questions

#TypeQuestionMitigation / DefaultOwnerDeadline
OQ-M2Open QuestionDoes CreateTicketScreen already accept a pre-filled customer argument, or is a small extension needed (MTCK-S02)?Confirm with Mobile Ticket Module; if missing, add an optional pre-fill arg to CreateTicketArgument.Mobile Squad2026-07-04
OQ-M3Open QuestionIs auto-association best done by the existing associateTicket() (PUT), or should the created ticket carry the contact in crm_properties at create time (single call)?Default: associate-on-return (proven). Single-call is a possible optimization later.Mobile + CRM BE2026-07-11
OQ-M4Open QuestionC360 vs CRM contact: confirm the correct identity + bloc path for each on auto-associate (Contact360AssociationBloc for C360).Default: branch by mode as the existing associate flow already does.Mobile Squad2026-07-04

PRD CHANGELOG

VersionDateBySectionTypeSummary
1.12026-06-26Decision settled14a, 15, Scope ChangesUPDATESettled OQ-M1 as decision MD-5: mobile keeps native data_source: "Mobile Flutter"; no embed_source on the native path (that mechanism is web-embed only). Removed OQ-M1; Backend scope is now firmly None.
1.02026-06-26Initial draftAllCREATEDCreated the mobile SUPPORT PRD for the cross-platform anchor. Background: CDP mobile is associate-existing only. Grounded against mobile-qontak-crm (contact "Others" tab other_tab.dart, native CreateTicketScreen + POST /v2.8/tickets, associate PUT /v2.8/tickets/{id} with qontak_customer_ids, the existing company/deal/product "From Existing/Create New" AssociationBottomSheet, and feature-flag + permission.update gating). Approach = native create + auto-associate (not webview). Stories MTCK-S01–S05-NEG with Gherkin ACs; cross-links the Mobile Ticket Module (TF-3053) AC1.4. NOT blocked by the CRM embed (that blocks only the web track).