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 andphase-4-…filename are unchanged to avoid re-keying Jira epic BOT-4341 — the "PH4" label is historical.
HEADER BLOCK
| Field | Value |
|---|---|
| PM | Dimas Fauzi Hidayat |
| PRD Version | 1.5 |
| Status | READY |
| PRD Type | PHASE |
| Epic | BOT-4341 |
| Squad | Hadiningbot Squad |
| Product Module | Bot, AI, and Automation |
| RFC Link | N/A — pending |
| Figma Master | Figma — AI Agent Send Attachment Action — pending; reference design exists in qontak-designer/app/components/bot-automation/actions/SendAttachmentDrawer.vue |
| Anchor | Qontak Action — ANCHOR |
| Labels | epic:qontak-chatbot | module:ai-agent | feature:send-attachment-action |
| Last Updated | 2026-06-15 |
Status values:
DRAFT→READY→BUILD→SHIPPEDREADY gate: Epic cannot move to In Progress in Jira without PRD Link + RFC Link populated.
Table of Contents
- HEADER BLOCK
- 2. CONDITIONAL BLOCK: PHASE CONTEXT
- 3. One-liner + Problem
- 4. Target Users + Persona Context
- 5. Non-Goals
- 6. Constraints
- 7. Feature Changes
- 8. New Features
- 9. API & Webhook Behavior
- 10. System Flow + User Stories + ACs
- 11. Rollout
- 12. Observability
- 13. Success Metrics
- 14. Launch Plan & Stage Gates
- 15. Dependencies
- 16. Key Decisions + Alternatives Rejected
- 17. Open Questions
- PRD CHANGELOG
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
| Persona | Role | Goal | Pain | Workaround |
|---|---|---|---|---|
| Primary — CS Ops Lead / Bot Manager | Customer-side bot owner who configures the AI Agent in Qontak Chatbot | Let 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 KB | The 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 human | Builds 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 summary | Asks "send me the price list" and gets recited text or a "please hold" instead of the actual PDF/image | Asks 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
- 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.)
- 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.)
- Sending files from the Knowledge Base — KB documents are not a source for this action in this phase. (Deferred.)
- Instagram channel — not supported by the underlying send path today; out of scope for this phase. (See Section 6.)
- 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.
- Customer-uploaded file handling — this action is outbound (agent → customer) only; receiving/parsing files from customers is out of scope.
- 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 Type | Retention Period | Cleanup Trigger | User-Visible Effect |
|---|---|---|---|
Pinned attachment file (Attachment record + stored file via chat_service_url/file_url) | Persisted for the life of the action | Builder removes the file from the action, or deletes the action / AI Agent | Agent 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 message | Standard 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 operation | File.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-designerfeat/ai-agent-actions— a dedicated Actions librarybot-automation/actions/index.vue, theAddActionDrawer/NewActionDrawerpicker, and per-action drawers onActionConfigBase+ActionFieldRow, includingSendAttachmentDrawer.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 sameAttachment+SendToChatServicedelivery 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 onlyaction_type === "api"(DrawerAction.vue); the Actions section + the Send Attachment action are not shipped (live only as theqontak-designerprototype). The send mechanism is reused; what's net-new is exposing it as a function-callable action.
Net-new vs Reused (for Engineering)
| Area | Status | Evidence |
|---|---|---|
| Send Attachment action + Actions-section config UI | 🆕 Net-new in production | chatbot-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 wiring | AiAgentTool 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 existing | Today only branches on action_type === "api" |
| Action picker / "Add action" list | ✏️ Change to existing | Add a "Send Attachment" entry |
| "Send attachment" bot-response function (the delivery itself) | ♻️ Reused as-is | send_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-is | Same 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).
| Element | Before | After |
|---|---|---|
| Action picker list | API 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-fetoday. They live only as theqontak-designerprototype (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
| # | Behavior | Entity Affected | Triggered By | Expected Behavior | Failure Behavior |
|---|---|---|---|---|---|
| 1 | Save Send Attachment action | AiAgentAction, AiAgentTool (node_type qontak_send_attachment), Attachment record(s) for pinned files | Builder clicks Save in the config drawer | Persist 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. |
| 2 | Agent invokes Send Attachment at runtime | Outbound conversation message (attachment) | AI Agent selects and calls the qontak_send_attachment tool during a conversation (function-calling), based on the trigger description + conversation context | Resolve the action's pinned Attachment record(s) → enqueue send via SendMessageImageWorker → CreateMessageByBot → 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 Story | Importance | Mockup / Technical Notes | Acceptance 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 Have | Implemented in SendAttachmentDrawer.vue:• AC-1: ActionConfigBase — default-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 ActionConfigBaseData 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 recordsBefore-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 Have | Figma: 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 actionBefore-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 Have | Figma: pending — reuse existing action enable/disable + agent train flow Data Fields: • enabled (bool) — User input• ai_agent_send_attachment_action (flag, default OFF) — Account configBefore-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]
| Dimension | Coverage | Notes |
|---|---|---|
| Boundary values | ⚠️ partial | ERR-2 covers oversize/unsupported; AC covers ≥1 file. ⚠️ QA: max file count per action, 0-byte file, filename length/Unicode |
| State transitions | ✅ defined | AC-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]
| Dimension | Coverage | Notes |
|---|---|---|
| Boundary values | ⚠️ partial | ERR-3 channel-limit; AC-2 multi-file. ⚠️ QA: max files per send per channel |
| State transitions | ✅ defined | Trigger-met → send → sent/failed events |
| Data validation | ✅ defined | ERR-2 channel_unsupported; ERR-3 channel_limit_exceeded |
| Concurrency | ⚠️ TBD | ⚠️ QA: agent fires action twice in rapid turns; duplicate-send guard |
| Network/timeout | ✅ defined | ERR-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 Name | Trigger | Properties |
|---|---|---|
ai_agent_send_attachment_action_created | Builder saves a new Send Attachment action | account_id, agent_id, action_id, file_count, content_message_mode, timestamp |
ai_agent_attachment_sent | Agent successfully sends attachment(s) | account_id, agent_id, action_id, channel, file_count, conversation_id, timestamp |
ai_agent_attachment_send_failed | Send fails after retries / pre-send guard | account_id, agent_id, action_id, channel, reason (5xx/channel_unsupported/channel_limit_exceeded/file_missing), timestamp |
ai_agent_attachment_action_triggered | Agent 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
| Stage | Audience | Duration | Success Gate to Advance | Owner |
|---|---|---|---|---|
| Internal Alpha | Qontak internal test accounts | 1–2 weeks | All S01–S03 ACs pass QA; send_success_rate ≥ 95% on WhatsApp + 1 other channel in test | PM + QA |
| Closed Beta | 3–5 design-partner AI Agent customers | 2–3 weeks | ≥ 3 customers configure & fire the action; send_success_rate ≥ 95%; no Sev-1/2 defects | PM + CSM |
| Open Beta | Opt-in, all active AI Agent accounts | 2–4 weeks | Adoption trending up; send_success_rate sustained ≥ 95% for 2 weeks; failure-reason mix understood | Eng Lead |
| GA | All plans with AI Agent enabled (flag ON by default) | Ongoing | All Open Beta gates sustained 2 weeks; PMM approved; monitoring + alerts live | PM + PMM |
15. Dependencies
| Dependency | Owning Team | Deliverable Needed | Blocking? |
|---|---|---|---|
Phase 1 Action Picker / AI Agent action-config surface (AiAgentAction, AiAgentTool, action drawer) | Hadiningbot Squad | Stable surface to register & enable the new action type; parameters.trigger field and action-selection behavior must not break | YES |
Existing attachment send path (SendMessageImageWorker → CreateMessageByBot → ChatService /messages/{channel}/bot) | Hadiningbot / ChatService (Core) | Reused as-is for outbound delivery + retry | YES |
Existing attachment upload + storage (Attachment model, attachment uploader, chat_service_url) | Hadiningbot / Core | Reused for pinning builder-uploaded files | YES |
| AI Service function-calling / tool execution | AI Service team | Execute the new qontak_send_attachment tool with its parameter schema | YES |
NodeRegistry node definition for qontak_send_attachment | Hadiningbot Squad | New node type registered (inputs/outputs/parameters) | YES |
v2 AI Agent capability/routing payload + CapabilityRefPresence validator | Hadiningbot Squad / AI Agent v2 | Schema 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 action | YES |
Design (Figma) frames from SendAttachmentDrawer.vue reference | UI/UX (Bulan / Rizky) | Dev-ready frames + config-UI copy reconciled to Section 6 limits | NO (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/reusedlabels 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
| Date | Decision | Rationale |
|---|---|---|
| 2026-06-15 | "Which file" is resolved by configuring multiple Send Attachment actions; the agent's existing action-selection (function-calling) chooses which to fire | Reuses the proven action framework; no new AI sub-selection logic; each action's trigger description is the selection signal |
| 2026-06-15 | Files are builder-uploaded and pinned per action (a scoped library), not a global media manager or dynamic URLs | Matches the reference design; minimal net-new surface; predictable, safe outbound content |
| 2026-06-15 | Reuse the existing attachment send path and retry (MAX_ATTEMPT = 3) unchanged | Reliability already battle-tested; lowest-risk delivery |
| 2026-06-15 | File types/size are the backend attachment.rb constants (system of record); channel-provider limits apply at send | Single authoritative source; avoids drift between UI copy and actual enforcement |
| 2026-06-15 | Native action requires no credential/approval flow; builder simply enables it in config | It's a first-party Qontak capability, not a cross-product credentialed action |
| 2026-06-15 | Configured as an action in the AI Agent Actions section (newest design), invoked via function-calling — exit conditions are not used | Aligns with the current AI Agent actions architecture; no legacy exit-condition routing |
| 2026-06-15 | The action reuses the existing "send attachment" bot-response function (Attachment + SendToChatService); delivery is not rebuilt | Lowest-risk reuse of a proven send path; tree-diagram text node was the reference for this function |
16b — Alternatives Rejected
| Alternative | Why Rejected | Date |
|---|---|---|
| Single action with a multi-file library + AI sub-selection of one file by description | Adds new selection logic and ranking ambiguity; the multi-action pattern achieves the same outcome with the existing framework | 2026-06-15 |
| Let the agent send arbitrary/dynamic file URLs it fetches | Safety + abuse risk (agent sending unvetted content); deferred | 2026-06-15 |
| Send files from the Knowledge Base | KB docs are retrieval context, not curated outbound deliverables; different lifecycle; deferred | 2026-06-15 |
| Build a new standalone media library UI | Heavier scope; existing upload pattern (MpUpload/MpUploadList) already covers the need | 2026-06-15 |
| Adopt the design's 10 MB / narrow-type copy as the real limit | Conflicts with backend ACCEPTEDFILESIZE (75 MB) and broader accepted types; would under-deliver vs. the platform | 2026-06-15 |
17. Open Questions
| # | Type | Question | Owner | Deadline |
|---|---|---|---|---|
| 1 | Open 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 + Eng | At build |
| 2 | Open Question | ANCHOR 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 |
| 3 | Assumption | The 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 Service | Closed beta |
| 4 | Assumption | Baseline 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 + PM | Closed beta |
| 5 | Risk | Agent 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 Service | Before GA |
| 6 | Risk | A 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). | Eng | Before GA |
| 7 | Open Question | In 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 Service | Before RFC |
PRD CHANGELOG
| Version | Date | By | Section | Type | Summary |
|---|---|---|---|---|---|
| 1.0 | 2026-06-15 | Claude | All | CREATED | Phase 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.1 | 2026-06-15 | Claude | FM,S1,S2,S7,S8,S9,S15 | MODIFIED | Reframed 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 status → draft for linter compliance |
| 1.2 | 2026-06-15 | Claude | CB,S7,S8,S16 | MODIFIED | Corrected 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.3 | 2026-06-15 | Claude | Header | STATUS | Scored 8.9/9.0 — all gates pass. Status DRAFT → READY (frontmatter in-review); ready for RFC |
| 1.4 | 2026-06-15 | Claude | S15,S17 | MODIFIED | Reconciled 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.5 | 2026-06-17 | Claude | FM, Title, Header, CB | RE-HOMED | Moved from the Mekari Action anchor to the Qontak Action initiative (action surface split by target + auth). Updated frontmatter anchor → qontak-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.6 | 2026-06-23 | Claude | S10 | MODIFIED | Mapped 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. |