Skip to main content

Qontak | Chatbot | AI Agent — Qontak Action — Send Attachment Action

Re-homed 2026-06-17: This action moved from the Mekari Action anchor into the Qontak Action initiative when the action surface was split by target + auth. Send Attachment is a Qontak-native action (no cross-product credential / HMAC), so it belongs with Qontak Action, not Mekari Action. Its SENDATT-PH4-* story ids and phase-4-… filename are unchanged to avoid re-keying Jira epic BOT-4341 — the "PH4" label is historical.

HEADER BLOCK

FieldValue
PMDimas Fauzi Hidayat
PRD Version1.5
StatusREADY
PRD TypePHASE
EpicBOT-4341
SquadHadiningbot Squad
Product ModuleBot, AI, and Automation
RFC LinkN/A — pending
Figma MasterFigma — AI Agent Send Attachment Action — pending; reference design exists in qontak-designer/app/components/bot-automation/actions/SendAttachmentDrawer.vue
AnchorQontak Action — ANCHOR
Labelsepic:qontak-chatbot | module:ai-agent | feature:send-attachment-action
Last Updated2026-06-15

Status values: DRAFTREADYBUILDSHIPPED READY gate: Epic cannot move to In Progress in Jira without PRD Link + RFC Link populated.

Table of Contents

2. CONDITIONAL BLOCK: PHASE CONTEXT

Anchor PRD: Qontak | Chatbot | AI Agent — Qontak Action — ANCHOR
(../qontak-action-anchor.md)
Initiative: Qontak Action — first-party actions in Qontak's own features, invoked via
function-calling, authenticated with the company's Qontak token. No
cross-product credential / HMAC / OAuth approval required.
Catalog item: Send Attachment action (one action in the Qontak Action catalog, not a
sequential phase).
Phase Goal: Enable a Qontak chatbot builder to give their AI Agent a native
"Send Attachment" action so the agent can autonomously send pinned
file(s) to the customer mid-conversation, without a human-built flow.

Sibling actions: Qontak CRM actions (create/update deal & ticket, create tag, assign agent,
resolve conversation) — same initiative, same action framework, company token.

This action: A NATIVE AI Agent action, "Send Attachment", configured in the AI Agent →
Actions section (newest design). The builder uploads the file(s) on the
action, writes a "when to use" trigger description, and an optional/AI-fillable
caption ("Content message"). At runtime the agent FUNCTION-CALLS the action
(no exit conditions); the action REUSES the existing "send attachment"
bot-response function to deliver the file(s) + caption over the conversation's
channel.

Deferred to next: AI sub-selection of an individual file from a multi-file library within
a single action (this action pins file(s); "which document" is resolved by
configuring multiple Send Attachment actions and letting the agent's action
selection choose). Dynamic/agent-fetched file URLs. Sending files from the
Knowledge Base. Instagram channel.

Cross deps: Shared AI Agent action-configuration surface (`AiAgentAction` / `AiAgentTool`,
the action drawer) — the "Send Attachment" action is registered and enabled
through the same flow used by the Qontak CRM actions. The `parameters.trigger`
("When and how to trigger this action?") field and action-selection
(function-calling) behavior MUST remain stable.

Reconciliation note (CB-P.3): Send Attachment is listed in the Qontak Action anchor's action catalog (not a sequential phase). It has no credential dependency (no HMAC / no SSO approval), so it ships independently of the Mekari Action credential work. It depends only on the shared action-configuration surface. See Section 17 (Open Questions) for the v2-capability-schema trigger-mapping question.

3. One-liner + Problem

One-liner: A native action that lets a Qontak AI Agent send the actual file or image to a customer, instead of only describing it in text.

Problem: When a customer asks for the price list, brochure, or product catalog, the AI Agent can only answer with text from its Knowledge Base — it describes or recites the information but cannot hand over the actual document or image. A customer who says "send me the price list" gets a typed-out summary instead of the PDF, which feels incomplete and still forces a human to send the real file. The "send attachment" capability exists in flow builder (the bot-response attachment field) but is not callable by the AI Agent, so the agent can explain knowledge yet never deliver the file itself. For full initiative context, see the Mekari Action ANCHOR PRD.

4. Target Users + Persona Context

PersonaRoleGoalPainWorkaround
Primary — CS Ops Lead / Bot ManagerCustomer-side bot owner who configures the AI Agent in Qontak ChatbotLet the agent hand over the actual file (price list PDF, brochure, catalog image) when a customer asks for it — not just a text answer from the KBThe agent answers file requests with KB text only; to send the real document the builder must build a rigid flow node or hand off to a humanBuilds a flow-builder "send attachment" node and routes to it, or has a human agent send the file manually. Doesn't compose with the AI Agent's natural-language reasoning.
Secondary — Customer (end recipient) (receives the attachment)The end customer chatting over WhatsApp/Telegram/etc.Receive the actual document/image they asked for, in-conversation, immediately — not a typed-out summaryAsks "send me the price list" and gets recited text or a "please hold" instead of the actual PDF/imageAsks repeatedly, or is told to check email/website.

(Full persona background: see ANCHOR PRD Section 2. See Section 6 for plan availability and feature flag scope.)

Design constraint inherited from the ANCHOR (Persona A): Configuration must be operable end-to-end by a non-technical builder — uploading a file, writing a plain-language trigger description, and saving. No knowledge of MIME types, file APIs, or channel limits required.

5. Non-Goals

  1. AI sub-selection inside one action — the agent will not pick one file out of a multi-file pool within a single action. File(s) are pinned per action; selecting between different documents is done by configuring multiple Send Attachment actions. (Deferred.)
  2. Agent-generated / dynamic file URLs — the agent cannot fetch an arbitrary URL or a file produced by another action and send it. Only builder-uploaded, pinned files. (Deferred.)
  3. Sending files from the Knowledge Base — KB documents are not a source for this action in this phase. (Deferred.)
  4. Instagram channel — not supported by the underlying send path today; out of scope for this phase. (See Section 6.)
  5. New standalone media/file library UI — this phase reuses the existing attachment upload pattern in the action config drawer; it does not build a central reusable media manager.
  6. Customer-uploaded file handling — this action is outbound (agent → customer) only; receiving/parsing files from customers is out of scope.
  7. Per-recipient personalization of the file — the same pinned file is sent to every customer the action fires for; no per-customer document generation.

6. Constraints

Platform: Configuration — Qontak web app (AI Agent action config drawer).
Runtime delivery — all attachment-capable channels supported by the
existing send path: WhatsApp, Telegram, Email, LINE, Twitter, Tokopedia
Chat. (Instagram NOT supported — see Non-Goals.)
Source: chatbot/lib/hub/chat_service/messages/create_message_by_bot.rb

Performance: Action dispatch (agent decision → enqueue send job) ≤ 2s.
Delivery is asynchronous via the existing SendMessageImageWorker path
with retry on 5xx (MAX_ATTEMPT = 3). Send failures surface as a logged
event (see Section 12), not a blocked conversation.

File types: INHERIT the existing send-attachment constants (system of record:
chatbot/app/models/attachment.rb).
Images: .jpg .jpeg .png .gif
Video: .mkv .mov .mp4
Audio: .opus .aac .amr .m4a .mp3
Documents: .pdf .doc .docx .xls .xlsx .ppt .pptx .csv .txt .zip
✅ DECIDED: file types/size align with the BACKEND (attachment.rb). The
reference design (SendAttachmentDrawer.vue) currently shows a NARROWER set
(JPG/PNG/WEBP/PDF/DOC/XLS) and "10 MB" — this is config-UI copy that MUST
be updated to match the backend constants below before/at build. See
Section 16 (Decision).

File size: System cap = 75 MB per file (ACCEPTEDFILESIZE = 78643200 bytes,
attachment.rb). Channel providers impose their own stricter per-type
limits (e.g. WhatsApp: image ~5 MB, video ~16 MB, document ~100 MB) which
ChatService enforces at send time. A file that passes the system cap but
exceeds a channel limit fails at send and is logged (Section 12).

Multiple files: A single Send Attachment action may pin and send multiple files in one
call (builder uploads ≥1 file; agent sends them together on trigger).

Plan scope: Inherit existing AI Agent entitlement — available to whatever plans
already have the AI Agent enabled. No new tier gating in this phase.

Feature flag: ai_agent_send_attachment_action | default: OFF. Enabled per account
(and rolled out per Section 11/14).

Read/write: Configure (upload files, set trigger/caption, enable action) — builder
roles that can already edit AI Agent configuration (Admin / Bot Manager).
Trigger at runtime — the AI Agent only (no human action). Customers
receive the file; they cannot trigger or configure it.

6.7 Data Lifecycle

Artifact TypeRetention PeriodCleanup TriggerUser-Visible Effect
Pinned attachment file (Attachment record + stored file via chat_service_url/file_url)Persisted for the life of the actionBuilder removes the file from the action, or deletes the action / AI AgentAgent can no longer send that file; action with zero files is invalid and cannot fire
Outbound message record (sent attachment in conversation)Same retention as any conversation messageStandard conversation/message retention policy (unchanged)None — appears in conversation history like any sent file
Transient send-time temp file (tmp/{title}.{ext} created by create_message_by_bot when streaming binary uploads)Seconds — single send operationFile.unlink immediately after upload completes (existing behavior)None

7. Feature Changes

Placement & approach (confirmed): This is configured in the AI Agent → Actions section (the newest design: qontak-designer feat/ai-agent-actions — a dedicated Actions library bot-automation/actions/index.vue, the AddActionDrawer/NewActionDrawer picker, and per-action drawers on ActionConfigBase + ActionFieldRow, including SendAttachmentDrawer.vue). "Send Attachment" is one action. At runtime the AI Agent function-calls the action (no exit conditions). The action reuses the existing "send attachment" bot-response function — the same Attachment + SendToChatService delivery used by the tree-diagram text node (which served only as the reference for that function, not as a build location).

New-vs-change verdict (verified against the repos): Production today (chatbot-fe) supports only action_type === "api" (DrawerAction.vue); the Actions section + the Send Attachment action are not shipped (live only as the qontak-designer prototype). The send mechanism is reused; what's net-new is exposing it as a function-callable action.

Net-new vs Reused (for Engineering)

AreaStatusEvidence
Send Attachment action + Actions-section config UI🆕 Net-new in productionchatbot-fe ships only the api form; Actions section + SendAttachmentDrawer exist only in qontak-designer (prototype)
Function-call binding: expose the send-attachment action as an AI-Agent tool🆕 Net-new wiringAiAgentTool model exists (2026-05-25); needs a tool entry so the agent can function-call this action
Action-type branching in DrawerAction.vue✏️ Change to existingToday only branches on action_type === "api"
Action picker / "Add action" list✏️ Change to existingAdd a "Send Attachment" entry
"Send attachment" bot-response function (the delivery itself)♻️ Reused as-issend_to_chat_service.rb, send_message_with_resolve.rb#send_attachment, SendMessageImageWorker — same function the text node uses
Attachment model + upload/storage (Attachment, IntentAttachment, chat_service_url, caption)♻️ Reused as-isSame infra behind the tree-diagram text-node attachment

Change ID: CHG-001 — "Send Attachment" appears in the action list

Change Type: Modified component (action picker / "Add action" list)
Surface: AI Agent config → Actions → Add action
Intent: Builder browses available actions to add to the agent.

Before:
- The action list offers API Integration only. No native "send a file" action exists.

After:
- A new "Send Attachment" entry appears (icon = attachment). Selecting it opens the
new Send Attachment config drawer (Section 8).
ElementBeforeAfter
Action picker listAPI Integration only+ "Send Attachment" entry
Action interface (store/ai-agent/interface.ts)action_type effectively api+ new action_type for send-attachment + attachment file references

Change ID: CHG-002 — DrawerAction.vue renders the new action type

Change Type: Modified component (action config router)
Surface: AI Agent config → action drawer
Intent: Render the correct config form for the selected action type.

Before:
- DrawerAction.vue branches on action_type === "api" only; any other type has no form.

After:
- DrawerAction.vue recognises the send-attachment action_type and renders the new
Send Attachment config form (Section 8). The "api" path is unchanged.
Figma: pending — net-new UI; reference prototype: qontak-designer/app/components/bot-automation/actions/SendAttachmentDrawer.vue

8. New Features

All items here are net-new in production. The Actions section + Send Attachment action config do not exist in chatbot-fe today. They live only as the qontak-designer prototype (feat/ai-agent-actions). The delivery is the existing bot-response send-attachment function — reused, not rebuilt (see Section 7 Reused table).

8.1 — Send Attachment action config drawer (Frontend, net-new)

Placement: AI Agent → Actions section → Add action → "Send Attachment"
Newest design (qontak-designer, feat/ai-agent-actions):
- app/pages/bot-automation/actions/index.vue (Actions library: name/status/usage table)
- app/components/bot-automation/actions/AddActionDrawer.vue + NewActionDrawer.vue (picker)
- app/components/bot-automation/actions/SendAttachmentDrawer.vue (this action's config)
- app/components/bot-automation/actions/ActionConfigBase.vue + ActionFieldRow.vue (shared shell)
Access: Builder roles that can edit AI Agent configuration (Admin / Bot Manager).
Status: Net-new in chatbot-fe (rendered via the extended DrawerAction.vue — see CHG-002).

Component Tree:

SendAttachmentDrawer (extends ActionConfigBase)
├── Action name — text, default "Send attachment" [from ActionConfigBase]
├── When and how to trigger — textarea (parameters.trigger), [from ActionConfigBase]
│ default: "When a customer requests a file, document, or image, such as asking
│ for a brochure, invoice, or photo." (builder editable)
├── Content message (ActionFieldRow) — caption sent alongside the file
│ ├── Mode toggle: "Let AI fill" (default) / "Set manually"
│ └── textarea — placeholder "Enter message to send alongside the attachment"
└── Add attachment* (MpUpload + MpUploadList) — builder uploads the pinned file(s)
├── multi-file upload, per-file remove
├── per-file icon by type, size subtitle, success/error status
└── helper text: accepted types + max size (must match Section 6 backend limits)

UI States:

Empty: No files uploaded. "Add attachment" required-error if builder saves with none:
"Please add at least one attachment."
Loading: File upload in progress — MpUploadList row shows uploading status.
Error: File exceeds size limit or unsupported type → row status = error with reason
(e.g. "File exceeds limit"). Save blocked until resolved.
Success: ≥1 valid file pinned; trigger description present → Save enabled.
Figma: pending — implement-to-pixel / mekari-taste can generate frames from this spec.
Reference component: SendAttachmentDrawer.vue (Pixel 3, token mode 2.4)

📊 UI State Diagram — Send Attachment config drawer

stateDiagram-v2
[*] --> Empty: Builder opens drawer (default name + trigger prefilled)
Empty --> Loading: Builder uploads file(s)
Loading --> Success: ≥1 valid file + trigger present
Loading --> Error: File too large / unsupported type
Error --> Loading: Builder removes/replaces file
Empty --> Error: Save with 0 files ("Please add at least one attachment.")
Success --> [*]: Save (action persisted)

8.2 — Function-call binding for the Send Attachment action (Backend, net-new wiring)

What: Expose the Send Attachment action as a function-callable tool for the AI Agent
(registered via AiAgentTool, pattern following existing qontak_* actions), so the
agent can invoke it through function-calling — NOT a from-scratch delivery engine.
Inputs: the configured action (its attachment + caption), optional AI-filled caption.
Behavior: On invocation, resolve the action's attachment(s) + conversation channel, then
dispatch through the REUSED "send attachment" bot-response function
(send_to_chat_service.rb / send_message_with_resolve#send_attachment →
SendMessageImageWorker → ChatService). The delivery is the existing function.
Reused: File delivery + retry = the existing bot-response send-attachment function, not rebuilt.

Engineering owns the tool/binding design + invocation contract; see the proposed API sequence in Section 9.

9. API & Webhook Behavior

#BehaviorEntity AffectedTriggered ByExpected BehaviorFailure Behavior
1Save Send Attachment actionAiAgentAction, AiAgentTool (node_type qontak_send_attachment), Attachment record(s) for pinned filesBuilder clicks Save in the config drawerPersist the action with its trigger description, content-message config (AI/manual), and pinned file reference(s). Uploaded files are stored via the existing attachment uploader (chat_service_url/file_url). On next agent train/sync, the action is sent to AI Service as a callable tool with its description/trigger.• Upload fails → block save, show per-file error.
• Save with zero files → block, "Please add at least one attachment."
• File too large / unsupported type → reject at upload.
2Agent invokes Send Attachment at runtimeOutbound conversation message (attachment)AI Agent selects and calls the qontak_send_attachment tool during a conversation (function-calling), based on the trigger description + conversation contextResolve the action's pinned Attachment record(s) → enqueue send via SendMessageImageWorkerCreateMessageByBot → ChatService POST /api/core/v1/messages/{channel}/bot with the file(s) + content-message caption. Customer receives the file(s) in-conversation.• 5xx from ChatService → retry up to MAX_ATTEMPT (3); if still failing, log ai_agent_attachment_send_failed.
• Channel does not support attachments / file exceeds channel limit → no send; log failure with reason.
• Pinned file deleted/missing → action no-ops; log failure.
[Claude to resolve during RFC: HTTP method/path for the new tool registration + action CRUD,
request/response JSON schema, NodeRegistry node definition (inputs/outputs/parameters),
and the function-calling parameter schema exposed to AI Service.]

📊 API Sequence — runtime send (PROPOSAL — Engineering owns final design)

This is a proposed call sequence to start the RFC conversation, not a prescriptive contract. Engineering decides the executor design, node schema, and exact service hops.

sequenceDiagram
participant AIS as AI Service (function-calling)
participant BE as Chatbot BE (qontak_send_attachment executor)
participant DB as DB (AiAgentAction / Attachment)
participant W as SendMessageImageWorker
participant CS as ChatService
AIS->>BE: invoke qontak_send_attachment(action_id, caption?)
BE->>DB: load action + pinned Attachment(s)
DB-->>BE: attachment refs (chat_service_url / file_url)
BE->>BE: validate channel supports attachments + file within limits
BE->>W: enqueue send (reused path)
W->>CS: POST /messages/{channel}/bot (file(s) + caption)
CS-->>W: 200 OK (or 5xx → retry ≤ MAX_ATTEMPT)
W-->>BE: result
BE-->>AIS: tool result (sent / failed + reason)

10. System Flow + User Stories + ACs

10.1 System Flow

Flow: AI Agent sends a pinned attachment to a customer
Type: User Journey + API Sequence

CONFIG (build time)
1. Builder opens AI Agent config → Actions → Add action → "Send Attachment".
2. Builder sets name, edits the "When and how to trigger this action?" description,
chooses content-message mode (Let AI fill / Set manually), and uploads file(s).
3. Builder saves → action persisted; on agent train/sync it is registered as a
callable tool (AiAgentTool) with its description/trigger.

RUNTIME (conversation)
4. Customer message arrives; AI Agent evaluates the conversation.
5. Agent's function-calling decides the Send Attachment action's trigger condition is
met → calls qontak_send_attachment(action_id, [optional caption]).
6. Backend resolves the action's pinned Attachment record(s) and the conversation channel.
7. Backend enqueues SendMessageImageWorker → CreateMessageByBot → ChatService
POST /messages/{channel}/bot with file(s) + content-message caption.
8. Customer receives the file(s) in-conversation; analytics event emitted.
9. FAILURE BRANCH — If ChatService returns 5xx → retry up to MAX_ATTEMPT (3).
If still failing, OR channel unsupported / file exceeds channel limit / file missing
→ no send; emit ai_agent_attachment_send_failed with reason. Conversation continues
(agent may fall back to text per its instructions); customer is not blocked.

📊 System Flow — AI Agent Send Attachment

sequenceDiagram
participant C as Customer
participant A as AI Agent (function-calling)
participant BE as Chatbot Backend
participant W as SendMessageImageWorker
participant CS as ChatService
C->>A: Message (e.g. "send me the brochure")
A->>A: Evaluate trigger descriptions, select Send Attachment action
A->>BE: call qontak_send_attachment(action_id, caption?)
BE->>BE: Resolve pinned Attachment(s) + conversation channel
alt Channel supports attachments & file within limits
BE->>W: Enqueue send job
W->>CS: POST /messages/{channel}/bot (file(s) + caption)
CS-->>W: 200 OK
W-->>BE: success
BE-->>C: Attachment delivered in conversation
Note over BE: emit ai_agent_attachment_sent
else 5xx from ChatService
W->>CS: retry up to MAX_ATTEMPT (3)
CS-->>W: still failing
Note over BE: emit ai_agent_attachment_send_failed (reason=5xx)<br/>agent may fall back to text
else Channel unsupported / file exceeds channel limit / file missing
Note over BE: no send; emit ai_agent_attachment_send_failed (reason)<br/>conversation continues
end

10.2 User Stories

User StoryImportanceMockup / Technical NotesAcceptance Criteria
[SENDATT-PH4-S01] — Builder configures a Send Attachment action

As a CS Ops Lead / Bot Manager, I want to add a "Send Attachment" action to my AI Agent with pinned file(s), a trigger description, and a caption, so that the agent can deliver that file automatically when the conversation calls for it.
Must HaveImplemented in SendAttachmentDrawer.vue:
• AC-1: ActionConfigBasedefault-name="Send attachment" + default-trigger prefilled
• AC-2: handleSave() emits save when fileEntries.length > 0 and trigger present
• AC-3: contentMode = ref("ai") default — save guard only checks manual mode
• AC-4: contentMode = "manual" + contentValue — blocked on save if empty
• ERR-1: attachError in handleSave() → "Please add at least one attachment."
• ERR-2: accept=".jpg,.jpeg,.png,.webp,.pdf,.doc,.docx,.xls,.xlsx" on MpUpload (⚠️ help text copy needs updating to match backend attachment.rb limits per §6)
• ERR-3: trigger field validated by ActionConfigBase

Data Fields:
name (string, required) — User input
parameters.trigger (string, required) — User input ("When and how to trigger this action?")
content_message (string, optional) — User input or AI-fill mode flag
content_message_mode (enum ai|manual) — User input
attachments[] (file refs, ≥1 required) — User upload → Attachment records

Before-After Behavior: Before: the AI Agent has no native action to send a file; builders rely on flow-builder nodes or human handoff. After: builders configure a reusable Send Attachment action the agent can call autonomously.
— Happy Path —
• AC-1: Given the AI Agent action list, when the builder selects "Send Attachment", then the config drawer opens with default name "Send attachment" and the default trigger description prefilled.
• AC-2: Given the drawer, when the builder uploads ≥1 supported file within size limits, sets a trigger description, and clicks Save, then the action is persisted and listed in the agent's actions.
• AC-3: Given the content-message field set to "Let AI fill", when the action is saved, then no static caption is required and the caption is generated by the agent at runtime.
• AC-4: Given the content-message field set to "Set manually", when the builder enters caption text and saves, then that exact caption is stored and used at send time.

— Error / Unhappy Path —
• ERR-1: Given the drawer with zero files, when the builder clicks Save, then save is blocked and "Please add at least one attachment." is shown.
• ERR-2: Given a file exceeding the size limit or of an unsupported type, when the builder uploads it, then the file row shows an error with the reason and Save is blocked until resolved.
• ERR-3: Given a missing trigger description, when the builder clicks Save, then save is blocked with a required-field error on the trigger field.

— Permission Model —
• CAN: Admin, Bot Manager (roles that can edit AI Agent config)
• CANNOT: Agent/viewer roles without AI Agent edit rights
• Unauthorized: "Add action" not rendered.

— UI States —
• Loading: file upload in progress (MpUploadList uploading row)
• Empty: no files; required error on Save
• Error: per-file error status with reason
• Success: ≥1 valid file + trigger present → Save enabled
[SENDATT-PH4-S02] — Agent sends the attachment when triggered

As an AI Agent (on behalf of the builder), I want to call the Send Attachment action and deliver the pinned file(s) to the customer over their channel, so that the customer receives the document they need without a human.
Must HaveFigma: N/A (runtime, no builder UI)

Data Fields:
action_id (string, required) — Agent function-call arg
caption (string, optional) — AI-generated (if AI-fill mode) or stored manual caption
channel (derived) — from the conversation room
attachment refs (derived) — pinned files on the action

Before-After Behavior: Before: the agent can only reply with text. After: the agent autonomously sends pinned file(s) + caption on the conversation channel via the existing send path.
— Happy Path —
• AC-1: Given an enabled Send Attachment action whose trigger condition is met, when the agent calls qontak_send_attachment, then the pinned file(s) are sent to the customer on the conversation's channel with the content-message caption.
• AC-2: Given an action with multiple pinned files, when the agent fires it, then all pinned files are sent in that turn.
• AC-3: Given content-message mode = "Let AI fill", when the action fires, then the agent generates a contextual caption and it accompanies the file.
• AC-4: Given a successful send, when delivery completes, then ai_agent_attachment_sent is emitted with action_id, channel, file_count, and conversation id.

— Error / Unhappy Path —
• ERR-1: Given ChatService returns 5xx, when the send is attempted, then it is retried up to MAX_ATTEMPT (3); if still failing, ai_agent_attachment_send_failed is logged with reason and the conversation continues.
• ERR-2: Given the conversation channel does not support attachments (e.g. Instagram), when the action would fire, then no file is sent and ai_agent_attachment_send_failed is logged with reason channel_unsupported.
• ERR-3: Given a pinned file exceeds the channel provider limit, when the send is attempted, then it fails gracefully and is logged with reason channel_limit_exceeded; the agent may fall back to text per its instructions.

— Permission Model —
• CAN: the AI Agent runtime only
• CANNOT: human agents/customers cannot trigger this action; a sent attachment cannot be recalled/unsent once delivered
• Unauthorized: action not exposed as a tool if disabled or behind the OFF feature flag.

— UI States —
• Loading: N/A (async send)
• Empty: N/A
• Error: failure logged; optional agent text fallback
• Success: attachment appears in the conversation like any sent file
[SENDATT-PH4-S03] — Builder enables/disables the action and rollout gating

As a CS Ops Lead / Bot Manager, I want to turn the Send Attachment action on or off for my agent (subject to the feature flag), so that I control whether the agent can send files and can stop it quickly if needed.
Should HaveFigma: pending — reuse existing action enable/disable + agent train flow

Data Fields:
enabled (bool) — User input
ai_agent_send_attachment_action (flag, default OFF) — Account config

Before-After Behavior: Before: no such action exists to toggle. After: the action can be enabled per agent and is gated by the account feature flag during rollout.
— Happy Path —
• AC-1: Given the feature flag is ON for the account, when the builder adds and enables the action, then on train it becomes a callable tool for the agent.
• AC-2: Given an enabled action, when the builder disables (or removes) it and re-trains, then the agent can no longer call it.

— Error / Unhappy Path —
• ERR-1: Given the feature flag is OFF for the account, when the builder opens the action list, then "Send Attachment" is not available (not rendered).

— Permission Model —
• CAN: Admin, Bot Manager
• CANNOT: roles without AI Agent edit rights
• Unauthorized: toggle not rendered.

— UI States —
• Loading: save/train in progress
• Empty: N/A
• Error: save failure toast
• Success: action shows enabled/disabled state

Dependencies: S02 depends on S01 (action must exist). S03 gates S01/S02 via feature flag.


NEGATIVE SCENARIOS (from Section 5 Non-Goals)

NEG-1: Given a single Send Attachment action with multiple pinned files,
When the builder expects the agent to choose ONE file by reasoning,
Then no per-file AI sub-selection occurs — all pinned files are sent
(file choice across documents = separate actions). [Non-Goal 1]

NEG-2: Given the agent obtains an external file URL from another source,
When it attempts to send that URL as an attachment,
Then no send occurs — only builder-uploaded pinned files are sendable. [Non-Goal 2]

NEG-3: Given a document in the Knowledge Base,
When the agent tries to send it via this action,
Then no send occurs — KB is not a source in this phase. [Non-Goal 3]

NEG-4: Given a conversation on Instagram,
When a Send Attachment action would fire,
Then no file is sent and ai_agent_attachment_send_failed (channel_unsupported)
is logged. [Non-Goal 4]

NEG-5: Given a customer sends a file TO the agent,
When the message is processed,
Then this action does not handle inbound files (outbound only). [Non-Goal 6]

🧪 Test Coverage Matrix — [SENDATT-PH4-S01]

DimensionCoverageNotes
Boundary values⚠️ partialERR-2 covers oversize/unsupported; AC covers ≥1 file. ⚠️ QA: max file count per action, 0-byte file, filename length/Unicode
State transitions✅ definedAC-1→AC-2 drawer open→save; ERR-1/3 block-on-invalid
Data validation⚠️ TBD⚠️ QA: special chars in name/caption, MIME spoofing (extension vs content type)
Concurrency⚠️ TBD⚠️ QA: two builders editing the same agent's actions simultaneously
Network/timeout⚠️ TBD⚠️ QA: upload interrupted mid-transfer; partial multi-file upload

🧪 Test Coverage Matrix — [SENDATT-PH4-S02]

DimensionCoverageNotes
Boundary values⚠️ partialERR-3 channel-limit; AC-2 multi-file. ⚠️ QA: max files per send per channel
State transitions✅ definedTrigger-met → send → sent/failed events
Data validation✅ definedERR-2 channel_unsupported; ERR-3 channel_limit_exceeded
Concurrency⚠️ TBD⚠️ QA: agent fires action twice in rapid turns; duplicate-send guard
Network/timeout✅ definedERR-1 5xx retry MAX_ATTEMPT(3) then logged failure

11. Rollout

Feature flag: ai_agent_send_attachment_action (see Section 6 — OFF by default)
Rollout: Stage 1 → Internal QA (Qontak internal test accounts)
Stage 2 → Closed beta: 3–5 design-partner AI Agent customers
Stage 3 → Open beta: opt-in for all active AI Agent accounts
GA → On by default for all plans with AI Agent enabled
Backward compat: Yes — additive. Existing flow-builder "send attachment" bot-response and
existing AI Agent actions are unaffected. No change to the send path itself.
Migration: None required — no schema migration of existing data; new action type +
attachment references only.

12. Observability

Key Events:

Event NameTriggerProperties
ai_agent_send_attachment_action_createdBuilder saves a new Send Attachment actionaccount_id, agent_id, action_id, file_count, content_message_mode, timestamp
ai_agent_attachment_sentAgent successfully sends attachment(s)account_id, agent_id, action_id, channel, file_count, conversation_id, timestamp
ai_agent_attachment_send_failedSend fails after retries / pre-send guardaccount_id, agent_id, action_id, channel, reason (5xx/channel_unsupported/channel_limit_exceeded/file_missing), timestamp
ai_agent_attachment_action_triggeredAgent decides to call the action (pre-send)account_id, agent_id, action_id, conversation_id, timestamp
Dashboard owner: Hadiningbot Squad (PM: Dimas Fauzi Hidayat)

Alerts:
- send_success_rate < 90% over a rolling 1h window → alert Hadiningbot on-call (Slack)
- ai_agent_attachment_send_failed with reason=5xx > 20 in 15 min → alert Eng on-call

12.6 Post-Launch Monitoring Cadence

Review cadence: Weekly for the first 4 weeks post-GA, then monthly.
Owner: Hadiningbot Squad (PM: Dimas Fauzi Hidayat)
Review scope: send_success_rate, send volume, action adoption, failure reasons mix.
Trigger thresholds:
- send_success_rate drops > 5 points week-over-week → investigate within 48h.
- channel_unsupported / channel_limit_exceeded failures > 15% of attempts → revisit
config-UI guidance and Section 6 limits.
Rollback consideration:
If send_success_rate < 80% sustained for 24h and unresolved, PM disables the
ai_agent_send_attachment_action flag for affected accounts while root-causing.

13. Success Metrics

Quality & Reliability:

⭐ Primary KPI: Attachment send success rate
Definition: ai_agent_attachment_sent / (ai_agent_attachment_sent +
ai_agent_attachment_send_failed) × 100
Baseline: N/A — new capability
Target: ≥ 95% within 30 days of GA (on supported channels)

Adoption & Usage:

- Action adoption
Definition: % of active AI Agents with ≥1 enabled Send Attachment action
Baseline: 0% — feature does not exist today
Target: ≥ 25% of active AI Agents within 60 days of GA
- Send volume
Definition: Count of ai_agent_attachment_sent per week
Baseline: 0
Target: Sustained week-over-week growth through the first 8 weeks post-GA

Efficiency & Impact:

- Human-handoff deflection for file requests
Definition: % of file-request conversations resolved by the agent sending a file
WITHOUT a human takeover (vs. baseline handoff rate for file requests)
Baseline: To be measured during closed beta (Assumption — see Section 17)
Target: ≥ 30% reduction in human handoffs on file-request conversations within
60 days of GA for adopting accounts

14. Launch Plan & Stage Gates

StageAudienceDurationSuccess Gate to AdvanceOwner
Internal AlphaQontak internal test accounts1–2 weeksAll S01–S03 ACs pass QA; send_success_rate ≥ 95% on WhatsApp + 1 other channel in testPM + QA
Closed Beta3–5 design-partner AI Agent customers2–3 weeks≥ 3 customers configure & fire the action; send_success_rate ≥ 95%; no Sev-1/2 defectsPM + CSM
Open BetaOpt-in, all active AI Agent accounts2–4 weeksAdoption trending up; send_success_rate sustained ≥ 95% for 2 weeks; failure-reason mix understoodEng Lead
GAAll plans with AI Agent enabled (flag ON by default)OngoingAll Open Beta gates sustained 2 weeks; PMM approved; monitoring + alerts livePM + PMM

15. Dependencies

DependencyOwning TeamDeliverable NeededBlocking?
Phase 1 Action Picker / AI Agent action-config surface (AiAgentAction, AiAgentTool, action drawer)Hadiningbot SquadStable surface to register & enable the new action type; parameters.trigger field and action-selection behavior must not breakYES
Existing attachment send path (SendMessageImageWorkerCreateMessageByBot → ChatService /messages/{channel}/bot)Hadiningbot / ChatService (Core)Reused as-is for outbound delivery + retryYES
Existing attachment upload + storage (Attachment model, attachment uploader, chat_service_url)Hadiningbot / CoreReused for pinning builder-uploaded filesYES
AI Service function-calling / tool executionAI Service teamExecute the new qontak_send_attachment tool with its parameter schemaYES
NodeRegistry node definition for qontak_send_attachmentHadiningbot SquadNew node type registered (inputs/outputs/parameters)YES
v2 AI Agent capability/routing payload + CapabilityRefPresence validatorHadiningbot Squad / AI Agent v2Schema must accept the new action under capabilities[].actions[] with required fields id, name, tool_name, action_type, action_type_version; validator must pass for the send-attachment actionYES
Design (Figma) frames from SendAttachmentDrawer.vue referenceUI/UX (Bulan / Rizky)Dev-ready frames + config-UI copy reconciled to Section 6 limitsNO (reference design exists)

📊 Dependency Graph (PROPOSAL — Engineering to validate blocking status in RFC)

Proposed view of how this phase depends on existing systems. Engineering owns the final blocking assessment; BLOCKING / reused labels below are the PM's starting position.

graph LR
F[Send Attachment Action - this phase]
F -->|BLOCKING| P1[Phase 1 action-config surface]
F -->|BLOCKING| NR[New qontak_send_attachment node + executor]
F -->|BLOCKING| AIS[AI Service function-calling]
F -->|reused| SP[Attachment send path - send_to_chat_service]
F -->|reused| UP[Attachment upload + storage]
F -->|non-blocking| FIG[Figma frames - prototype exists]

16. Key Decisions + Alternatives Rejected

16a — Decisions Made

DateDecisionRationale
2026-06-15"Which file" is resolved by configuring multiple Send Attachment actions; the agent's existing action-selection (function-calling) chooses which to fireReuses the proven action framework; no new AI sub-selection logic; each action's trigger description is the selection signal
2026-06-15Files are builder-uploaded and pinned per action (a scoped library), not a global media manager or dynamic URLsMatches the reference design; minimal net-new surface; predictable, safe outbound content
2026-06-15Reuse the existing attachment send path and retry (MAX_ATTEMPT = 3) unchangedReliability already battle-tested; lowest-risk delivery
2026-06-15File types/size are the backend attachment.rb constants (system of record); channel-provider limits apply at sendSingle authoritative source; avoids drift between UI copy and actual enforcement
2026-06-15Native action requires no credential/approval flow; builder simply enables it in configIt's a first-party Qontak capability, not a cross-product credentialed action
2026-06-15Configured as an action in the AI Agent Actions section (newest design), invoked via function-calling — exit conditions are not usedAligns with the current AI Agent actions architecture; no legacy exit-condition routing
2026-06-15The action reuses the existing "send attachment" bot-response function (Attachment + SendToChatService); delivery is not rebuiltLowest-risk reuse of a proven send path; tree-diagram text node was the reference for this function

16b — Alternatives Rejected

AlternativeWhy RejectedDate
Single action with a multi-file library + AI sub-selection of one file by descriptionAdds new selection logic and ranking ambiguity; the multi-action pattern achieves the same outcome with the existing framework2026-06-15
Let the agent send arbitrary/dynamic file URLs it fetchesSafety + abuse risk (agent sending unvetted content); deferred2026-06-15
Send files from the Knowledge BaseKB docs are retrieval context, not curated outbound deliverables; different lifecycle; deferred2026-06-15
Build a new standalone media library UIHeavier scope; existing upload pattern (MpUpload/MpUploadList) already covers the need2026-06-15
Adopt the design's 10 MB / narrow-type copy as the real limitConflicts with backend ACCEPTEDFILESIZE (75 MB) and broader accepted types; would under-deliver vs. the platform2026-06-15

17. Open Questions

#TypeQuestionOwnerDeadline
1Open Question✅ RESOLVED — file types/size align with backend attachment.rb (75 MB + full type list). Remaining build task: update SendAttachmentDrawer.vue UI copy (currently "10 MB" + narrow types) to match, and decide whether to surface per-channel provider limits inline.UX + EngAt build
2Open QuestionANCHOR Phase Index sequencing — should this native action be re-sequenced ahead of planned Phases 2–3 (credential rotation/audit), since it has no credential dependency?PM (anchor owner)Before READY
3AssumptionThe agent's function-calling reliably distinguishes between multiple Send Attachment actions using their trigger descriptions (correct file picked). Mitigation: validate during closed beta with multi-action configs; provide trigger-writing guidance.PM + AI ServiceClosed beta
4AssumptionBaseline human-handoff rate for file-request conversations is measurable to size the deflection KPI. Mitigation: instrument and measure during closed beta before committing the GA target.Data + PMClosed beta
5RiskAgent sends the wrong file or sends a file in an inappropriate context (over-eager triggering), harming customer trust. Mitigation: builder-authored trigger descriptions + closed-beta tuning; per-account feature flag for fast disable; ai_agent_attachment_action_triggered event to audit trigger decisions; document trigger-writing best practices.PM + AI ServiceBefore GA
6RiskA channel/provider limit or unsupported channel causes silent non-delivery, leaving the customer without the file. Mitigation: pre-send guard + ai_agent_attachment_send_failed with explicit reason; agent text fallback per instructions; alert on success-rate drop (Section 12).EngBefore GA
7Open QuestionIn the live v2 AI Agent model, actions live under capabilities[].actions[] with validated fields id, name, tool_name, action_type, action_type_version (per CapabilityRefPresence); this set does not include a trigger field. Where does the action's "when to use" trigger description map in the v2 capability schema? Confirm with AI Agent v2 owners.PM + AI ServiceBefore RFC

PRD CHANGELOG

VersionDateBySectionTypeSummary
1.02026-06-15ClaudeAllCREATEDPhase 4 PRD created from coaching session; grounded in BE (chatbot), FE (chatbot-fe), and design (qontak-designer) repos — action framework, attachment limits/channels, and SendAttachmentDrawer.vue reference design
1.12026-06-15ClaudeFM,S1,S2,S7,S8,S9,S15MODIFIEDReframed problem (send the file vs. describe KB text); tightened one-liner to ≤25 words; verified new-vs-change in repos and restructured S7 (Feature Changes = 2 modifications) + S8 (net-new: config UI + node/executor) with a Net-new vs Reused table; added proposed API sequence (S9) + dependency graph (S15) as engineering proposals; added S02 "cannot recall" CANNOT line; frontmatter statusdraft for linter compliance
1.22026-06-15ClaudeCB,S7,S8,S16MODIFIEDCorrected placement to AI Agent Actions section per newest design (qontak-designer feat/ai-agent-actions); clarified trigger = function-calling (no exit conditions); reframed S8.2 from "new node/executor" to a function-call binding that reuses the existing send-attachment bot-response function (tree-diagram text node was reference only); added decisions in S16
1.32026-06-15ClaudeHeaderSTATUSScored 8.9/9.0 — all gates pass. Status DRAFT → READY (frontmatter in-review); ready for RFC
1.42026-06-15ClaudeS15,S17MODIFIEDReconciled with latest chatbot BE (post-fetch): added dependency on the v2 AI Agent capability/routing payload + CapabilityRefPresence validator (action fields id/name/tool_name/action_type/action_type_version), and Open Question on where the "when to use" trigger maps in the v2 capability schema
1.52026-06-17ClaudeFM, Title, Header, CBRE-HOMEDMoved from the Mekari Action anchor to the Qontak Action initiative (action surface split by target + auth). Updated frontmatter anchorqontak-action, title, header Anchor row, and the CONDITIONAL BLOCK phase context (now a catalog item under Qontak Action, company-token auth, sibling to the Qontak CRM actions — no HMAC/credential dependency). Story ids (SENDATT-PH4-*) and filename unchanged to preserve the BOT-4341 Jira key.
1.62026-06-23ClaudeS10MODIFIEDMapped SendAttachmentDrawer.vue implementation to SENDATT-PH4-S01 Technical Notes (UI/UX story only): AC-1→AC-4 and ERR-1→ERR-3 each linked to the implementing ref/function in the prototype. S02 (runtime) and S03 (feature flag) left without component link — no UI surface.