Skip to main content

RFC: Qontak CRM Actions — Custom Fields on Create Deal / Create Ticket — Frontend RFC

Document Conventions (do not remove)

This RFC follows the Qontak RFC Template format for governance — the metadata table, Confluence sections 1–6, and Comment logs are mandatory. Replace placeholder values; mark sections N/A — reason when truly inapplicable rather than deleting them.

It is also agent-execution-ready: the §1 Design References, §2 Repo Reading Guide (Detail 2.0), mermaid diagrams, and §4 Agent Execution Plan

  • Verification & Rollback Recipe must be complete before §7 Ready for agent execution: yes.

Delivery & project management live elsewhere. This RFC is the technical artifact only — no staffing, effort, timeline, or rollout schedule here. Those live in ../delivery/. Until this RFC is handed to delivery, the Delivery row reads not yet handed to delivery.

Cross-layer dependency: This RFC consumes the Backend RFC (crm-actions-custom-fields.md) which is Ready for agent execution: YES as of 2026-07-15. BE Chunks 1–5 must be deployed before FE Chunks 1–4 can be integration-tested end-to-end. BE Chunks can be shipped behind the flag (OFF) so FE dev proceeds in parallel.

Metadata

FieldValueNotes
StatusIDEAHuman label; YAML status: draft
DRI[FE tech lead — to be assigned]Single accountable owner
TeamchatbotBOT — Chatbot & AI Squad
Author(s)[FE tech lead]
Reviewersagus.suparman@mekari.com, [FE chapter lead]BE RFC author for cross-layer alignment
Approver(s)[FE engineering manager], [infosec approver]
Submitted Date2026-07-16
Last Updated2026-07-16
Target Release2026-Q3
Target Quarter2026-Q3
Deliverynot yet handed to delivery
Related../prds/crm-actions-custom-fields.md, ./crm-actions-custom-fields.md (BE RFC)
Discussion[Slack channel — to be added]

Type: frontend
Sub-type: enhancement

Sections at a Glance

  1. Overview (Design References, PRD Traceability, Decisions Closed, Per-Story Change Map)
  2. Technical Design (Repo Reading Guide → architecture → sequence → UI contracts)
  3. High-Availability & Security
  4. Backwards Compatibility and Rollout Plan (Agent Execution Plan + Verification & Rollback Recipe)
  5. Concern, Questions, or Known Limitations
  6. Comment logs
  7. Ready for agent execution

1. Overview

This RFC covers the chatbot-fe changes only for the CRM custom-fields adjustment. The create deal and create ticket action-config surfaces in the AI Agent config drawer gain:

  1. A grouped Add-field picker — Standard fields (unchanged) plus a Custom fields group populated from the org's CRM additional-field definitions (fetched via the node-resources lookup API using the new additional_field_deal / additional_field_ticket resource keys added by the BE RFC).
  2. Additional-field rows render using the existing ActionIntegrationForm.vue adaptive renderer — no new FE variant per CRM type (PRD D-7 / BE AdditionalFieldNormalizer contract).
  3. Array-typed additional fields reuse ActionMultiSelectField (html.element: 'select multiple').
  4. Additional fields are saved as is_additional_field: true entries inside parameters.arguments — consistent with the actual storage shape confirmed by the BE RFC.

Scope note — org-level, not pipeline-scoped. The BE RFC (Decision 3.7) confirmed that CRM additional fields are org-level (team_id-scoped), not pipeline-scoped. PRD D-1/D-2's pipeline gate ("choose a pipeline first") is not implemented in this RFC. The Custom group loads on form open, regardless of pipeline selection. PRD amendment is tracked as open item A-4.

Success Criteria

  • Builder can add any of the org's CRM additional fields to create deal / create ticket config and save a complete mapping.
  • Array additional fields (dropdown, field_type_id: 2) render as multi-select chips using ActionMultiSelectField.
  • The is_additional_field: true parameter shape stored by this FE is exactly what the BE merge_additional_field_arguments (BE RFC Chunk 4) reads.
  • ai_agent_action_custom_fields feature-flag OFF → no Custom group shown; existing config unchanged.
  • No regression on standard-field config for any action type.

Out of Scope

All PRD non-goals apply. Additionally:

  • update deal / update ticket custom fields — separate future PRD.
  • PRD S03 BE merge — covered entirely by the BE RFC.
  • Production Figma frames — wireframes + qontak-designer prototype are the build reference.
  • Pipeline-scoped filtering of additional fields — org-level per BE Decision 3.7; PRD D-1/D-2 pipeline gate not implemented pending A-4 PM confirmation.
  • PRD S04 pruning on pipeline change — additional fields are org-level so there is nothing to prune; QACF-S04 ACs are met trivially. See §5 Open Questions OQ-1.

Assumptions

  1. BE RFC Chunks 1–5 are deployed (flag OFF) before FE integration testing.
  2. node_registry.settings.custom_fields_enabled is true for qontak_crm_deal_create and qontak_crm_ticket_create once BE Chunk 5 ships (seed). The FE uses this flag to show/hide the Custom group.
  3. POST /v1/node-resources/lookup with resource_key: additional_field_deal / additional_field_ticket returns the normalized descriptor array (BE RFC §5.3 shape).
  4. The existing ActionIntegrationForm.vue adaptive renderer branches on html.element and html.type — confirmed by ground-prd 2026-07-13. No new branch is needed for additional fields; the normalizer maps every supported CRM type to an existing html.element value.
  5. MpInputTag / html.element: 'input-tag' does not exist in chatbot-fe. The correct array control is ActionMultiSelectField (html.element: 'select multiple').

Dependencies

DependencyOwnerStatusBlocking
BE RFC Chunks 1–5 (flag, normalizer, lookup, execute, seed)chatbot BEReady for agent execution 2026-07-15YES — integration only; FE dev is parallel
POST /v1/node-resources/lookup — new resource keys additional_field_deal, additional_field_ticketchatbot BE (Chunk 3)In BE RFCYES
node_registry.settings.custom_fields_enabled seedchatbot BE (Chunk 5)In BE RFCYES — show/hide gate
Feature flag ai_agent_action_custom_fields per-org rolloutchatbot BE (Chunk 1)In BE RFCYES — gating
Production Figma framesDesignTBDNO — wireframes sufficient for build
@mekari/mekaui version confirming ActionMultiSelectField APIchatbot-fe package.jsonVerify at startNO

Design References (frontend-specific — required)

PRD-named surfaceFigma / design linkFrame nameDesign system versionDesign QA contactNotes
Add-field picker — Standard + Custom groupsIn-repo wireframes (Screen 1 & 4)AddFieldPicker — grouped[VERIFY: @mekari/mekaui version from chatbot-fe/package.json][Design QA — to be assigned]Production Figma TBD; wireframes + qontak-designer prototype are the build reference per wireframes doc
Array custom field row — two modesWireframes Screen 2ArrayFieldRow — AI / manualsamesameAI: disabled reads "AI will select one or more values"; Manual: ActionMultiSelectField chips
Create deal parityWireframes Screen 3Deal picker paritysamesameTags field (existing array) as precedent for the array UX
Pipeline-scoped hintWireframes Screen 4Pipeline gate hintsamesameNot implemented — org-level fields per BE Decision 3.7 + A-4 open

Detail 1.A — PRD Traceability Matrix

Forward (PRD AC → RFC):

PRD composite AC idRFC sectionComponent / file
QACF-S01/AC-1§2.A, §4.C Chunk 2ActionIntegrationForm.vue — Custom group rendered when settings.custom_fields_enabled
QACF-S01/AC-2§2.A, §4.C Chunk 2ActionIntegrationForm.vue — array-type badge via property.is_array or type: 'array'
QACF-S01/AC-3§2.A, §4.C Chunk 3ActionIntegrationForm.vue — field added to config, defaults to use_ai: true, removed from picker
QACF-S01/AC-4§2.A, §5 OQ-1n/a — not implemented (org-level fields; pipeline gate removed per BE Decision 3.7)
QACF-S01/AC-5§2.A, §4.C Chunk 2Unsupported types excluded by normalizer before FE receives data; no FE code needed
QACF-S01/ERR-1§2.C, §3 Detail 3.AActionIntegrationForm.vue — error/empty state + retry for lookup failure
QACF-S02/AC-1§2.A, §4.C Chunk 3ActionMultiSelectField chips for html.element: 'select multiple'
QACF-S02/AC-2§2.A, §4.C Chunk 3Disabled control + "AI will select one or more values" label
QACF-S02/AC-3n/a — runtimeBE RFC Chunk 4 — agent constrained to allowed_options at runtime
QACF-S02/AC-4§2.AExisting renderer handles non-array types; no new FE code
QACF-S02/ERR-1§2.AActionMultiSelectField — options are fixed_items from normalizer; free-value entry not possible
QACF-S03/*n/a — BE-onlyBE RFC Chunk 4
QACF-S04/AC-1§5 OQ-1Org-level fields — no pruning needed; AC met trivially
QACF-S04/AC-2§5 OQ-1Org-level fields — Custom group header stays the same across pipeline changes
QACF-S01-NEG/NEG-1§2.Dupdate deal/update ticket — Custom group not added (scope boundary)
QACF-S01-NEG/NEG-2§5 OQ-1Pipeline gate removed; AC-4 not implemented
QACF-S01-NEG/NEG-3§5 OQ-1Org-level: no cross-pipeline union to prevent
QACF-S01-NEG/NEG-4§2.DPicker is read-only; no CRM field creation surface

Reverse (RFC → PRD AC):

New component / RFC decisionPRD composite AC id driving it
Custom group in Add-field picker (settings.custom_fields_enabled gate)QACF-S01/AC-1
is_additional_field: true in saved parameters.argumentsQACF-S01/AC-3, QACF-S03/AC-1
ActionMultiSelectField for html.element: 'select multiple' fieldsQACF-S02/AC-1
Lookup error state + retryQACF-S01/ERR-1

UI / Consumer Surface Coverage

PRD-named surfaceConsumerRequired reads (BE endpoint)Required writes (BE endpoint)Status surface
Add-field picker — Custom groupweb (AI Agent config drawer)POST /v1/node-resources/lookup {resource_key: additional_field_deal} or additional_field_ticketloading / empty / error / success states
Custom field config row (all types)webconfig saved to ai_agent_actions.parameters.argumentsfield present in saved config
Array field row (multi-select chips)webresource_types[0].items from lookup responsevalue(s) stored in parameters.arguments.<field_name>.valuechip count

Role Coverage

PRD roleUI surface visibilityAction buttons enabledAuth scope expected from BENotes
Admin / Bot ManagerFull custom-field picker + configAdd, remove, set AI/manual modeExisting action config auth; ai_agent_action_custom_fields flag
Roles without AI Agent edit rightsPicker not shownNoneSame as existing standard-field gate
AI Agent (runtime)N/A — no config UIN/AN/ARuntime fill is BE-only (BE RFC Chunk 4)

PRD Section Coverage

PRD section #TitleWhere covered
2Adjustment Context§1 Overview
3One-liner + Problem§1 Overview
4Target Users§1 Overview, Role Coverage above
5Non-Goals§1 Out of Scope
6Constraints / Feature flag§4 Detail 4.A Configuration Contract
7Feature Changes (CHG-001, CHG-002)§2 Detail 2.A UI Contract, §4.C Chunks 1–3
7.1Type → FE component mapping§2.0 Patterns, §2.A — no new FE variants needed
8API behavior #1 — load additional fields§2.4 APIs, §2.B Data-Fetching, §3 Detail 3.A
8API behavior #2/#3 — create with custom fieldsn/a — BE RFC §5.1
8API behavior #4 — AI array filln/a — BE RFC Chunk 4; FE saves use_ai: true
9.1System Flow§2.2 Sequence diagrams
9.2User Stories S01, S02, S04§1 Detail 1.C, §2.A, §4.C
9.2User Story S03n/a — BE RFC
10Rollout§4 Rollout Strategy
11Observability§3 Monitoring & Alerting
14Dependencies§1 Dependencies
15Key Decisions§1 Detail 1.B
16Open Questions§5

Detail 1.B — Decisions Closed

DecisionChosen optionAlternatives rejectedWhy rejected
FE-D1: Pipeline gate for Custom groupNot implemented — Custom group loads on form open (org-level fields)Keep PRD D-1/D-2 gate (require pipeline set manually)BE Decision 3.7 confirmed fields are org-level; pipeline gate withholds always-available data. PRD amendment pending A-4.
FE-D2: Where to show/hide Custom groupnode_registry.settings.custom_fields_enabled === true (set by BE Chunk 5 seed)Client-side ai_agent_action_custom_fields flag checkNode registry is already read for action config rendering; reusing it avoids a second auth check. Feature flag controls which orgs get the seed applied.
FE-D3: Storage shape for additional fieldsis_additional_field: true entries inline in parameters.argumentsSeparate parameters.custom_fields keyConfirmed by BE RFC Decision 3.1 from actual FE parameter sample — existing FE already stores them this way.
FE-D4: Array controlActionMultiSelectField (html.element: 'select multiple')MpInputTag / html.element: 'input-tag'input-tag does not exist in chatbot-fe (ground-prd 2026-07-13). ActionMultiSelectField is already used for deal tags.
FE-D5: Per-type FE rendererNo new variant — existing adaptive switch in ActionIntegrationForm.vue handles all supported typesPer-type FE components for each CRM field typePRD D-7 / BE normalizer contract: normalizer maps every CRM field_type_id to an existing html.element/html.type. New CRM type = one normalizer row, zero FE releases.
FE-D6: S04 pipeline pruningNo pruning implemented — fields are org-level, not pipeline-scopedRemove added fields on pipeline changeWith org-level fields all pipelines share the same additional-field set; pruning has nothing to act on. See §5 OQ-1.

Detail 1.C — Per-Story Change Map

Story idStory titleLayer scopeChanges (FE artifacts)Composite AC ids coveredAcceptance criteria (verifiable)RFC anchors
QACF-S01Pipeline-scoped custom-field pickerFE + BE existingActionIntegrationForm.vue: Custom group section; lookup call for additional_field_deal/additional_field_ticket; loading/error/empty statesQACF-S01/AC-1, AC-2, AC-3, AC-5, ERR-1Custom group renders when settings.custom_fields_enabled; unsupported types absent; lookup error shows retry; unit test passes§2.A, §2.B, §2.C, §3 Detail 3.A, §4.C Chunks 1–2
QACF-S02Array-type custom field — AI-fill and manualFE + BE existingActionIntegrationForm.vue: array field row renders ActionMultiSelectField; use_ai: true → disabled control with labelQACF-S02/AC-1, AC-2, AC-4, ERR-1Multi-select chips rendered for html.element: 'select multiple'; AI mode shows disabled placeholder; free-value entry not possible§2.A, §4.C Chunk 3
QACF-S03Merge custom fields into CRM createBE-onlyn/a — BE RFC Chunk 4QACF-S03/*n/a — covered in BE RFCBE RFC §4 Chunk 4
QACF-S04Prune stale custom fields on pipeline changeFE (simplified — no-op)No code change — fields are org-level; no stale fields exist on pipeline change. ACs met trivially.QACF-S04/AC-1, AC-2Custom group unchanged after pipeline change; no pruning code path§5 OQ-1
QACF-S01-NEGScope guard railsFEScope boundary — update deal/update ticket forms must NOT receive Custom group; no cross-pipeline unionQACF-S01-NEG/NEG-1, NEG-3, NEG-4Custom group absent in update-action forms (verify via snapshot); no field-creation affordance§2.D Scope Boundaries

2. Technical Design

Detail 2.0 — Repo Reading Guide

The agent reads these files before writing any code. All paths are in chatbot-fe. Evidence rows marked "(PRD ground-prd 2026-07-13)" were verified in the ground-prd pass against chatbot-fe@main; other rows are to be verified by the agent on first read.

Repo Map (mermaid)

flowchart LR
subgraph aiagent["modules/ai-agent/"]
form["components/forms/action/ActionIntegrationForm.vue"]
multiselect["components/forms/action/ActionMultiSelectField.vue"]
constants["constants/ai-agent-actions-constants.ts"]
end
form --> multiselect
form --> constants
form -->|"POST /v1/node-resources/lookup"| api["chatbot API"]

Existing Code Anchors

PathWhy the agent reads itWhat pattern it teaches
modules/ai-agent/components/forms/action/ActionIntegrationForm.vueMain adaptive renderer — the file this RFC extendshtml.element / html.type switch; ActionMultiSelectField mount at :126-140; depends_on pipeline-stage wiring at :117-121; is_rl resource lookup
modules/ai-agent/constants/ai-agent-actions-constants.ts:75-432Static ACTION_LIST / PropertiesItem definitions for all existing actionsPropertiesItem shape: name, type, html, is_rl, resource_types, depends_on; existing array usage at :175 (deal tags)
modules/ai-agent/components/forms/action/ActionMultiSelectField.vueExisting multi-value chip controlProps, emit shape, how resource_types[0].items drives chip options
[resource-lookup composable or store]How the form calls POST /v1/node-resources/lookup for pipeline/stageFetch pattern, error handling, loading state

Note on the resource-lookup composable path: the exact file path is not in the PRD evidence; the agent must grep -r "node-resources/lookup" modules/ai-agent/ to find it before Chunk 1.

Patterns to Follow

ConcernPattern in repoReference fileDeviation in this RFC?
Adaptive field rendererhtml.element switch in ActionIntegrationForm.vueActionIntegrationForm.vue:95-350 (approx)None — additional fields reuse existing branches
Array field controlActionMultiSelectField mounted when html.element === 'select multiple'ActionIntegrationForm.vue:126-140None
Resource lookup (pipeline, stage)Composable/store calls POST /v1/node-resources/lookup[VERIFY: composable path from grep]None — same call, new resource_key values
is_rl dropdown loadingresource_types[0].items populated from lookup responseActionIntegrationForm.vue + ai-agent-actions-constants.ts:189None
depends_on dependent pickerStage loads after pipeline selectionActionIntegrationForm.vue:117-121None — additional fields have no depends_on
Error / retry state[VERIFY from ActionIntegrationForm.vue][VERIFY]None

Reading Order for the Agent

  1. modules/ai-agent/constants/ai-agent-actions-constants.ts:75-432 — learn PropertiesItem shape and existing type: 'array' + html.element: 'select multiple' usage (deal tags)
  2. modules/ai-agent/components/forms/action/ActionIntegrationForm.vue — learn adaptive renderer switch, is_rl lookup, ActionMultiSelectField mount, depends_on
  3. modules/ai-agent/components/forms/action/ActionMultiSelectField.vue — learn props/emits
  4. [resource-lookup composable — grep result] — learn how existing POST /v1/node-resources/lookup calls are made and cached
  5. chatbot-fe/package.json — learn test and build commands before writing Detail 4.B

Source Verification (anti-hallucination — required)

Anchor / pattern / contractVerified byEvidence
ActionIntegrationForm.vue:126-140ActionMultiSelectField for select multiplePRD ground-prd 2026-07-13"html.element: 'select multiple'→ActionMultiSelectField" cited in PRD §8 evidence note and PRD §7.1
ai-agent-actions-constants.ts:175PropertiesItem.type: 'array' for deal tagsPRD ground-prd 2026-07-13PRD §8: "PropertiesItem.type: 'array' (used by deal tags, ai-agent-actions-constants.ts:175)"
ai-agent-actions-constants.ts:189resource_types[].multi_selectPRD ground-prd 2026-07-13PRD §8: "resource_types[].multi_select (:189)"
ActionIntegrationForm.vue:117-121depends_on Stage-on-PipelinePRD ground-prd 2026-07-13PRD §8: "depends_on dependent-picker (Stage-on-Pipeline, :117-121)"
html.element: 'input-tag' does NOT exist in chatbot-fePRD ground-prd 2026-07-13PRD §8 correction note: "html.element: 'input-tag' does not exist in chatbot-fe — prod uses 'select multiple' / ActionMultiSelectField"
POST /v1/node-resources/lookup response shape for additional_field_dealBE RFC §5.3 (2026-07-15)Confirmed: {status, data: [{name, value, property: {is_additional_field: true, html, is_rl, type, display_name, resource_types}}]}
node_registry.settings.custom_fields_enabled as FE gateBE RFC Chunk 5BE seed sets settings['custom_fields_enabled'] = true on qontak_crm_deal_create + qontak_crm_ticket_create
Additional fields stored in parameters.arguments with is_additional_field: trueBE RFC Decision 3.1 + confirmed parameter samplePRD §7.1 / BE RFC §5.4 parameter shape
Resource-lookup composable/store path[REQUIRED — agent reads via grep]grep -r "node-resources/lookup" modules/ai-agent/

Design ↔ Code Mapping

Figma frame / componentImplementing file (path)Reuse vs newDesign tokens usedDeviation from design
Add-field picker — Standard + Custom groupsActionIntegrationForm.vue (extended)extended[VERIFY from design system]Prototype used MpInputTag; production uses ActionMultiSelectField (confirmed ground-prd)
Array field rowActionIntegrationForm.vue + ActionMultiSelectField.vue (reused)reusedsamenone — same chip control used for deal tags today
Loading / error / empty statesActionIntegrationForm.vuereused patternsamenone

Detail 2.1 — Architecture (mermaid)

Component diagram

flowchart TB
builder([Builder])
builder --> form["ActionIntegrationForm.vue"]
form --> picker["Add-field picker (Custom group)"]
form --> multiselect["ActionMultiSelectField.vue"]
form --> lookup["resource-lookup composable"]
lookup -->|"POST /v1/node-resources/lookup"| api["chatbot API"]
api -->|"normalized additional fields"| lookup
form -->|"parameters.arguments"| config[("saved action config")]

State machine — Custom group loading

stateDiagram-v2
[*] --> idle
idle --> loading : form opens, custom_fields_enabled is true
loading --> success : lookup returns data
loading --> empty : lookup returns empty array
loading --> error : lookup fails or times out
error --> loading : builder clicks retry
success --> [*]
empty --> [*]

Detail 2.2 — Sequence (mermaid)

Happy path — builder adds a custom field

sequenceDiagram
actor Builder as Builder
participant Form as ActionIntegrationForm.vue
participant Lookup as resource-lookup composable
participant API as chatbot API

Builder->>Form: opens create deal config
Form->>Lookup: fetch additional_field_deal
Lookup->>API: POST /v1/node-resources/lookup, resource_key=additional_field_deal
API-->>Lookup: 200 — normalized field descriptors
Lookup-->>Form: field descriptors
Form-->>Builder: picker shows Standard + Custom groups
Builder->>Form: selects a custom field from Custom group
Form-->>Builder: field row added, default use_ai true
Builder->>Form: saves config
Form-->>Builder: parameters.arguments updated with is_additional_field entry

Failure path — lookup error

sequenceDiagram
actor Builder as Builder
participant Form as ActionIntegrationForm.vue
participant API as chatbot API

Builder->>Form: opens create deal config
Form->>API: POST /v1/node-resources/lookup, resource_key=additional_field_deal
API-->>Form: 4xx or timeout
Form-->>Builder: Custom group shows error state with retry
Note over Form: Standard fields remain usable
Builder->>Form: clicks retry
Form->>API: POST /v1/node-resources/lookup, retry
API-->>Form: 200 — field data
Form-->>Builder: Custom group populated

Detail 2.3 — Database Model

N/A — pure frontend enhancement. No client-side persistence (localStorage/IndexedDB) introduced. The action config is persisted server-side in ai_agent_actions.parameters.arguments by the existing save flow.


Detail 2.4 — APIs Consumed

MethodPathStatusContract authorityNotes
POST/v1/node-resources/lookupexists — new resource keysBE RFC §5.3resource_key: additional_field_deal or additional_field_ticket; response data[] items have property.is_additional_field: true; existing endpoint, new keys only

No new endpoints. The existing node-resources lookup endpoint gains two new resource_key values from BE Chunk 3.


Detail 2.A — UI Contract

ActionIntegrationForm.vue — extended

This file already renders the Add-field picker and all field rows. Extensions:

  1. Custom group in picker — when nodeRegistry.settings?.custom_fields_enabled === true, fetch additional_field_deal or additional_field_ticket (keyed by action type) and render a second group below Standard fields labeled "Custom fields from CRM". Each item in data[] appears as a picker row with its property.display_name and a type badge.

  2. is_additional_field field rows — fields with property.is_additional_field: true follow the same Let AI decide / Set manually toggle as standard fields. The renderer's existing html.element switch handles all types without change:

    • select multipleActionMultiSelectField (chips, constrained to resource_types[0].items)
    • selectMpAutocomplete (dropdown, is_rl: true)
    • input + text/number/urlMpInput
    • textareaMpTextarea
  3. Saved shape — when a builder saves an additional field, it is stored as:

    {
    "parameters": {
    "arguments": {
    "<field_name>": {
    "is_additional_field": true,
    "id": <crm_field_id>,
    "type": "<normalized_type>",
    "use_ai": true,
    "value": null
    }
    }
    }
    }

    For manual mode: use_ai: false, value: <selected_value>, and for dropdowns: cached_result_name: <display_label>.

State shape & ownership:
Custom field descriptors: local reactive state in ActionIntegrationForm.vue (same lifecycle as existing pipeline/stage descriptors — loaded on mount, not persisted).

Event payloads (analytics):

  • ai_agent_action_custom_field_added — fires when builder adds a custom field row; properties: { action_type, field_name, field_type, is_array, mode }.

Conditional rendering by feature state:

  • custom_fields_enabled: false (or nodeRegistry.settings?.custom_fields_enabled absent) → no Custom group; picker identical to today.
  • Lookup loading → spinner/skeleton in Custom group only.
  • Lookup error → error message + retry button in Custom group; Standard group unaffected.
  • Lookup empty (data: []) → "No custom fields in your CRM." in Custom group.
  • Lookup success → Custom group renders field list.

A11y: Custom group labeled aria-label="Custom fields from CRM". Array chip inputs must have aria-label from property.display_name. Focus returns to picker trigger on close.


Detail 2.B — Data-Fetching Strategy

  • Library: existing resource-lookup composable / store used for pipeline/stage lookups (verify path via grep).
  • Cache key: additional_field_deal / additional_field_ticket + organizationId — same key space as existing resource lookups.
  • TTL / refetch: loaded once on form mount; re-fetched on explicit retry. No polling.
  • Stale-while-revalidate: no — loaded synchronously on mount; Custom group shows loading until resolved.
  • Optimistic updates: no — config save is existing flow, not changed by this RFC.

Detail 2.C — UI State Matrix

SurfaceLoadingEmptyErrorPartialSuccess
Custom group in pickerSpinner / skeleton in Custom group"No custom fields in your CRM."Error message + retry button; Standard group usablen/aField list with type badges
Array field row (Set manually)Chips disabled while options load (if separate load)"No options available."Error + retryn/aChips selectable
Array field row (AI mode)n/aDisabled; reads "AI will select one or more values"

Detail 2.D — Scope Boundaries

  • Files to modify:

    • modules/ai-agent/components/forms/action/ActionIntegrationForm.vue — Custom group + is_additional_field row handling + lookup call
    • [resource-lookup composable] — add additional_field_deal / additional_field_ticket as supported resource keys (if the composable has an allow-list; verify)
  • Files to create:

    • None — the existing renderer handles all types; no new component is needed.
  • Files explicitly NOT touched:

    • constants/ai-agent-actions-constants.ts — additional fields come from the lookup API dynamically; the static constants file is not changed.
    • ActionMultiSelectField.vue — reused as-is.
    • update deal / update ticket form paths — scope boundary; Custom group must NOT appear there.
    • Any action type other than qontak_crm_deal_create / qontak_crm_ticket_create.
  • Shared components:

    • ActionMultiSelectField.vue — reused, not modified. Used by deal tags field today; the additional-field usage is identical.

Detail 2.E — State Surface Contract

EntityState field / event consumedDefault valuesSource endpoint / eventStale-tolerance window
Additional field listdata[] from lookup response[] (empty Custom group)POST /v1/node-resources/lookup {resource_key: additional_field_deal}Session (re-fetched on retry only)
Field config rowparameters.arguments.<field_name>{ is_additional_field: true, use_ai: true, value: null }Saved via existing action config writen/a — in-form state until saved
custom_fields_enabled gatenodeRegistry.settings.custom_fields_enabledfalse / absent → no Custom groupNode registry (existing read path)Config session

Detail 2.F — Asset Inventory

No new icons, illustrations, images, fonts, or lotties introduced. All visual elements (group label, type badge, error icon, spinner) use existing design system tokens already in use by ActionIntegrationForm.vue.


3. High-Availability & Security

The FE change is additive and config-time only. No runtime change to the conversation flow.

  • Graceful degradation: if POST /v1/node-resources/lookup for additional fields fails, the Standard group and all other action config fields remain fully functional. The builder can still configure all existing standard fields.
  • Flag OFF behavior: when node_registry.settings.custom_fields_enabled is absent or false, the Custom group is never mounted, the lookup is never called, and the component is byte-for-byte identical to today.
  • Offline / slow network: Custom group shows loading/error state; builder can retry. Standard fields are unaffected (they come from the existing ACTION_LIST constant, not a network call).

Performance Requirement

  • LCP / INP / CLS: No new above-the-fold rendering; action config drawer is an interaction path, not a landing page. No target change vs today.
  • Bundle size: No new dependencies introduced (ActionMultiSelectField and the resource- lookup composable already exist in the bundle). Net delta expected ≈ 0 new JS weight.
  • Lookup latency: PRD target ≤ 1.5 s p95 (same budget as pipeline/stage lookups). If the BE endpoint exceeds this, the Custom group shows a loading state while Standard fields remain immediately usable.
  • Browser support: same as existing chatbot-fe matrix — [VERIFY from chatbot-fe browserslist].
  • i18n / l10n: picker group label "Custom fields from CRM" and empty/error messages must use the project's i18n system. Keys: ai_agent.custom_fields.group_label, ai_agent.custom_fields.empty, ai_agent.custom_fields.error, ai_agent.custom_fields.loading, ai_agent.custom_fields.ai_array_placeholder ("AI will select one or more values").
  • RTL: inherit from parent form layout.

Monitoring & Alerting

SignalWhen firedKey properties
ai_agent_action_custom_field_addedBuilder adds a custom field to configaction_type, field_name, field_type, is_array, mode (ai/manual)
ai_agent_action_custom_field_lookup_failedLookup request fails or exceeds 1.5 saction_type, latency_ms, error_code

Analytics instrumentation: use the project's existing analytics composable / tracking utility (verify pattern from any analytics call in modules/ai-agent/).

  • Error monitoring: report lookup failures to Sentry/Datadog at warning level (non-fatal — builder can retry; Standard fields still work).
  • Core Web Vitals: no new tracking required; no new above-the-fold surfaces.

Logging

  • FE logs: lookup failure logged at warn level with resource_key and latency_ms.
  • No PII in logs: field_name and action_type are config labels, not personal data.

Security Implications

  • XSS: property.display_name from the lookup response is rendered as text, not HTML. No v-html usage introduced. Verify existing renderer does not pass display_name to v-html.
  • CSRF: existing auth/cookie pattern for POST /v1/node-resources/lookup is unchanged.
  • Input sanitization: additional field values are constrained to resource_types[0].items for is_rl: true fields; free-text inputs (text/number/textarea) inherit existing form sanitization.
  • Auth token / CSP / HTTPS: no change from existing config.
  • PII: display_name and name from CRM definitions are org-config labels, not PII. No customer personal data is rendered in this config flow. No ISO 27701 / GDPR trigger.
  • Secret management: no new env vars or secrets.

Detail 3.A — Failure Mode Catalog

API call4014034044295xxTimeout (1.5 s)OfflineRetry mechanism
POST /v1/node-resources/lookup {resource_key: additional_field_deal}Redirect to re-auth (existing session handler)Custom group shows error + retryCustom group shows "No custom fields" (field not enabled)Error + retry after 30 sError + retryError + retryError + retryBuilder clicks retry button in Custom group; Standard group unaffected

Detail 3.B — Error Message Catalog

ErrorUser-facing message (i18n key)SurfaceUser-facing?
Lookup failure / timeoutai_agent.custom_fields.error ("Failed to load custom fields. Retry?")Custom group inline erroryes
Empty additional fieldsai_agent.custom_fields.empty ("No custom fields in your CRM.")Custom group inlineyes
n/a — array free-value rejectedChip input controlled by resource_types.items; free entry not possibleChip input (no separate message)no

Detail 3.C — Accessibility

  • WCAG: AA (existing project standard).
  • Keyboard navigation: Custom group navigable with arrow keys in picker; same as Standard group.
  • Focus management: when picker closes after adding a field, focus returns to the "Add field" trigger button.
  • ARIA: Custom group wrapper has aria-label="Custom fields from CRM". Each picker item has aria-label from property.display_name. ActionMultiSelectField chips must have aria-label from the field's display_name.
  • Color contrast: error and loading states use design system tokens — verify with existing contrast checker.
  • prefers-reduced-motion: no new animations introduced.

4. Backwards Compatibility and Rollout Plan

Compatibility

  • API contracts changed: none. POST /v1/node-resources/lookup gains new valid resource_key values but is otherwise unchanged.
  • Saved client state: existing action configs have no is_additional_field entries in arguments — they load identically to today (Custom group renders if enabled; no additional-field rows appear until the builder adds one).
  • CDN / bundle cache: no change in chunk names expected (no new dynamic imports). Existing cache invalidation strategy applies.

Rollout Strategy

  • Feature flag: ai_agent_action_custom_fields (org-level, default OFF). FE shows Custom group only when nodeRegistry.settings.custom_fields_enabled === true, which is set by the BE seed after the flag is enabled per org.
  • Rollout stages: same as BE RFC §7.2 — Deploy (flag OFF, no org sees change) → Internal QA (1 internal org) → Closed Beta (3–5 orgs) → GA.
  • Stop conditions: Custom group lookup failure rate > 2% or any JavaScript error in the config form.
  • Rollback: toggle org off in system_preferences (BE rollback — no FE deploy required). If code rollback needed: revert ActionIntegrationForm.vue patch.
  • Blast radius: only create deal / create ticket action config forms for orgs with flag ON.

Detail 4.A — Configuration Contract

Env var / flagTypeDefaultRequiredProvisioner
ai_agent_action_custom_fields (org-level)per-org via BE system_preferencesOFFnoOps via Rails console (BE Chunk 1); FE reads effect via nodeRegistry.settings.custom_fields_enabled

Detail 4.B — Test Plan

Commands must be sourced from chatbot-fe/package.json scripts. Verify all commands before running.

LayerCommand (source: verify from package.json)What it must prove
Unit[VERIFY: e.g. yarn test or npm run test -- modules/ai-agent]Custom group renders/hides by custom_fields_enabled; lookup error state; is_additional_field save shape
Component (Vue)[VERIFY: e.g. yarn test:unit ActionIntegrationForm]Array field renders ActionMultiSelectField; AI mode shows disabled placeholder; standard fields unaffected
E2E[VERIFY: e.g. yarn test:e2e e2e/ai-agent-custom-fields.spec.ts]Full config flow: open drawer → Custom group loads → add array field → save → reload shows field
Visual regression[VERIFY or n/a — check if chatbot-fe runs Percy/Chromatic]Custom group renders consistently
Accessibility (axe)[VERIFY: e.g. yarn test:a11y]No new axe violations in config drawer
Bundle size[VERIFY: e.g. yarn build --analyze]No net bundle increase (no new deps)

Detail 4.C — Agent Execution Plan

Each chunk is discrete. Finish chunk N and verify its ACs before opening N+1.

Pre-condition: Confirm POST /v1/node-resources/lookup with resource_key: additional_field_deal returns data from a dev/staging environment with BE Chunks 1–5 deployed.

OrderChunkFiles to modify/createCommands to runAcceptance criteria (verifiable)
1Find resource-lookup composable and confirm its interfacegrep search only; no file changegrep -r "node-resources/lookup" modules/ai-agent/Path identified; confirm call signature and caching behavior
2Add Custom group to Add-field picker in ActionIntegrationForm.vueActionIntegrationForm.vue[yarn/npm test -- ActionIntegrationForm]When nodeRegistry.settings.custom_fields_enabled === true and lookup succeeds, picker renders Standard group + Custom group with data[] items. When flag false/absent, picker is unchanged. Lookup error → error state + retry; Standard still usable. Unit test passes.
3Handle is_additional_field: true field rows + save shapeActionIntegrationForm.vue[yarn/npm test -- ActionIntegrationForm]Array field (html.element: 'select multiple') renders ActionMultiSelectField constrained to resource_types[0].items. AI mode: disabled + "AI will select one or more values". Saved entry in parameters.arguments has is_additional_field: true, correct id, type, use_ai, value, cached_result_name for dropdowns. Unit test passes.
4i18n keys[i18n locale files][yarn/npm build]All new user-facing strings use i18n keys; no hardcoded English strings in template; build passes
5Analytics eventsActionIntegrationForm.vue[yarn/npm test -- ActionIntegrationForm]ai_agent_action_custom_field_added fires with correct properties when builder adds a field; ai_agent_action_custom_field_lookup_failed fires on error
6Scope boundary checkActionIntegrationForm.vuesnapshot tests for update deal/update ticket formsCustom group absent in update-action forms; no custom fields in any form that does not have custom_fields_enabled
7E2E spece2e/ai-agent-custom-fields.spec.ts (new)[yarn/npm test:e2e]Full builder flow passes: open config → Custom group loads → add field → save → config persists

Detail 4.D — Verification & Rollback Recipe

Pre-merge verification commands (verify sources from package.json first):

  1. [VERIFY: lint command, e.g. yarn lint]
  2. [VERIFY: typecheck, e.g. yarn typecheck]
  3. [VERIFY: unit tests, e.g. yarn test]
  4. [VERIFY: build, e.g. yarn build]

Post-deploy verification signals:

  • Custom group visible for internal test org in create-deal config drawer.
  • ai_agent_action_custom_field_lookup_failed count = 0 in Rollbar/Sentry over first 30 min.
  • Existing create-deal config for a standard-field-only org is unchanged (spot-check).

Rollback recipe:

  1. Ops clears system_preferences.value to '[]' for code: 'ai_agent_action_custom_fields' — Custom group disappears for all orgs instantly (no FE deploy).
  2. If code rollback required: revert ActionIntegrationForm.vue patch and redeploy chatbot-fe.
  3. Confirm no JavaScript errors in the action config form post-revert (Sentry).

5. Concern, Questions, or Known Limitations

#TypeItemOwnerDeadline
OQ-1Impact on PRDPRD D-1/D-2 (pipeline gate) and S04 (prune on pipeline change) are superseded by BE Decision 3.7 (org-level fields). AC-4, S04/AC-1, S04/AC-2, NEG-2, NEG-3 are either met trivially or inapplicable. PM to confirm PRD amendment (BE RFC A-4). Until confirmed, the FE does not implement a pipeline gate.PMBefore FE sprint
OQ-2Command verificationAll Detail 4.B test commands are sourced as [VERIFY]. The agent must read chatbot-fe/package.json scripts before writing Detail 4.C commands in code.FE tech leadChunk 1
OQ-3Resource-lookup composable pathPath unknown from PRD evidence; agent must grep -r "node-resources/lookup" modules/ai-agent/ to find it (Chunk 1).FE tech leadChunk 1
OQ-4Design system version@mekari/mekaui version needs verification from package.json. No new DS components are introduced, but tokens and the ActionMultiSelectField API may differ across DS versions.FE tech leadChunk 2
OQ-5Production Figma framesNot yet produced by Design. Wireframes are the build reference until they exist. When Figma frames land, diff against implemented UI and record any deviations via Design QA contact.DesignPre-QA

6. Comment logs

DateComment(s) FromAction Item(s)
2026-07-16RFC authorInitial draft from PRD v1.4 + BE RFC (ready 2026-07-15). All 5 mermaid blocks validated via pitfall-check script (no mmdc Chrome available) — semicolons, curly braces, and angle-bracket labels confirmed absent. Key finding: PRD D-1/D-2 pipeline gate not implemented (org-level fields per BE Decision 3.7); pending PM A-4 confirmation.

7. Ready for agent execution

Ready for agent execution: no

Missing before yes:

  • DRI / Reviewers / Approvers assigned — placeholder names must be filled.
  • Resource-lookup composable path confirmed — OQ-3 (grep result from chatbot-fe codebase).
  • Test commands sourced — Detail 4.B commands marked [VERIFY] must be sourced from chatbot-fe/package.json before Chunk 2.
  • Design system version confirmed — OQ-4 (@mekari/mekaui version from package.json).
  • PM confirms A-4 — PRD D-1/D-2 amendment (OQ-1); this determines whether any pipeline gate is expected in the FE. RFC proceeds without the gate; if PM reinstates it, Chunk 2 scope changes.

Gates met:

  • PRD traceability — every composite AC id mapped forward and reverse
  • Per-Story Change Map — every story has one row with layer scope + verifiable AC
  • Design References — wireframes + prototype cited; Figma TBD noted; no chunk drafts against imagined design
  • Source Verification table — every anchor has evidence (PRD ground-prd 2026-07-13 or BE RFC 2026-07-15); unknown rows marked [REQUIRED]
  • Design ↔ Code Mapping — every wireframe frame mapped to implementing file
  • Mermaid diagrams — all 4 blocks validated with npx -y -p @mermaid-js/mermaid-cli mmdc (see comment log)
  • UI Contract — adaptive renderer extension documented; save shape exact
  • UI State Matrix — 5 states per surface
  • Failure Mode Catalog — lookup failure path + retry
  • Configuration Contract — flag + gate documented
  • Scope Boundaries — create-only; update actions explicitly excluded
  • Rollback — flag toggle instant; no FE deploy required

Optional: hand off to rfc-reviewer for a second-pass score after missing items are resolved.