Qontak | Chatbot & AI | Midtrans Native Integration — Phase 1: P0 Core Actions
Midtrans provider — Phase 1 (P0 Core Actions) PRD under the Native Integration ANCHOR. Midtrans is the inaugural payment provider of the Native Integration umbrella (folded in from the former standalone
midtrans-native-integration/initiative on 2026-06-17). Imported from Confluence.
HEADER BLOCK
| Field | Value |
|---|---|
| PM | Dimas Fauzi Hidayat |
| PRD Version | 1.4 |
| Status | DRAFT |
| PRD Type | NEW |
| Epic | BOT-4340 |
| Squad | BOT |
| RFC Link | Planned — RFC to be created and linked once aligned with engineering team |
| Figma Master | Pending — this phase adds one net-new config panel (MidtransActionConfigPanel, see §6) rendered inside the existing action drawer, following the Google Calendar native-integration panel pattern; no other screens change. Figma frame not yet opened — RFC to confirm whether the Google Calendar panel component is reused as-is or a Midtrans variant frame is needed. |
| Anchor | Native Integration — ANCHOR — Midtrans provider (Confluence) |
| Labels | epic:chatbot-ai | module:ai-agent | feature:midtrans-native-integration |
| Last Updated | 2026-07-03 |
2. One-liner + Problem
One-liner: Give Qontak Admins/SPVs native Midtrans payment actions — status check, payment link, cancel, refund — that the AI Agent executes in-conversation with zero API wiring.
Problem: Qontak admins and supervisors who want to automate payment-related customer interactions via the AI Agent are currently forced to use the generic API Integration action menu, which requires manual configuration of API endpoints, headers, request body, and authentication credentials — a process too technical for non-developer users. As a result, routine payment tasks (checking payment status, generating payment links, processing cancellations and refunds) remain dependent on human agents despite being fully automatable. The cost today is concrete: on the Midtrans-paying accounts this PRD targets (Sales Suite Plus, Ultimate, and Qontak 360 — see §5 plan scope), every one of these four payment tasks requires a human CS agent to leave the conversation, work the Midtrans dashboard, and relay the result back by hand — so payment queries queue behind agent availability, cannot be resolved outside staffed hours, and the AI Agent hands over exactly the conversations (payment confirmation at the point of purchase) where autonomous real-time resolution closes sales. No baseline volume metric exists yet for these accounts (evidence gap — beta sizing tracked against the ≥5-CID target in §11).
3. Target Users + Persona Context
| Persona | Role | Goal | Pain | Workaround |
|---|---|---|---|---|
| Primary — Admin/SPV | Qontak admin or supervisor responsible for configuring AI Agent flows on behalf of their company | Configure Midtrans payment actions in the AI Agent without developer involvement | Generic API Integration action menu requires manual URL, header, body, and auth configuration — too technical for non-developer admins | Use the API Integration action menu with developer assistance, or escalate payment queries entirely to human CS agents |
4. Non-Goals
- Midtrans dashboard UI embedded in Qontak — no in-app transaction management view in this phase.
- Support for multiple Midtrans accounts per workspace — single account connection only.
- Manual trigger by human agents — all P0 actions are AI Agent-initiated only in this phase.
- P1 billing & subscription actions (create_snap_token, create_invoice, get_invoice, void_invoice, create_subscription, get_subscription, cancel_subscription) — deferred to Phase 2.
- P2 advanced actions (get_transaction_history, capture_transaction, direct_refund, update_subscription) — deferred to Phase 3.
5. Constraints
- Platform: AI Agent configuration — Web only. Conversation output — Web + Mobile (read-only on mobile).
- Performance: AI Agent action must complete within 60 seconds. Requests exceeding 60s are timed out.
- Data limits: Aligned with Midtrans API limits per endpoint (to be confirmed during RFC).
- Plan scope: Sales Suite Plus, Ultimate, and Qontak 360 only. Not available on Starter or Free plans.
- Feature flag: TBD (to be aligned with engineering team) | default: OFF
- Read/write: Admin/SPV: Can configure, edit, and disconnect. AI Agent: Can execute. CS Agent/Human: Read-only.
- Auth: All actions use Bearer token via the shipped BOT-4243
midtrans_oauth2auth_type (chatbot/config/auth_providers/midtrans_oauth2.yml:1-6; enum in app/models/organization_connection.rb:24). Redis-cached token,ttl_seconds: 3300(~55 min). Credentials: environment (sandbox/production, required), client_id (required), client_secret (required, secret). No other credential fields exist.
6. New Features
This phase introduces 4 new Midtrans native action types in the AI Agent action builder (Bot & Automation → Actions). Each appears as an independent entry in the action type selector, following the Google Calendar native integration UX pattern.
Design status (mirrors Header Figma Master): this is a net-new config panel (MidtransActionConfigPanel) rendered inside the existing action drawer — no other screens change. The Figma frame is pending; the RFC will confirm whether the Google Calendar native-integration panel component is reused as-is or a Midtrans variant frame is opened.
Shared UX: URL: /bot-automation/actions. Access: Admin and SPV on eligible plans with flag ON.
Shared component tree (all 4 action config panels):
- MidtransActionConfigPanel — net-new panel hosted in the existing action drawer; one instance per Midtrans action type
- ActionNameField (text, required) — names the action as it appears in the Actions list
- TriggerDescriptionField (textarea, required) — tells the AI Agent when to invoke this action in conversation
- MidtransAuthBlock — manages the single per-org Midtrans connection (shared across all 4 action types)
- ConnectionStatusIndicator — shows "Not connected" / "Midtrans connected [environment]"
- ConnectButton (if not connected) — opens EnvironmentSelector (sandbox/production), ClientIdField, ClientSecretField (matching the shipped
midtrans_oauth2provider fields — chatbot/config/auth_providers/midtrans_oauth2.yml:3-6) - ReauthorizeButton (if connected) — re-enters credentials to refresh the connection (S01 AC-4)
- DisconnectButton (if connected) — severs the org-level Midtrans connection
- DynamicFieldsSection (action-specific; schemas in the 4 Action tables below; confirmed during RFC) — request fields the AI Agent needs per action type
Shared UI States:
- Loading: Skeleton loader while auth status is fetched
- Empty: All fields blank; auth block shows "Not connected" with ConnectButton; Save disabled
- Error: connection failure — "Failed to connect to Midtrans. Check your credentials and try again." Save disabled. If the auth-status fetch itself fails on panel load: "Failed to load configuration. Try again." + Retry button (S01 ERR-3), Retry returns to Loading.
- Success: "Midtrans connected [environment]" shown; Save enabled on required fields filled
State transitions: panel opened → Loading. Auth status fetched and org not connected → Empty. Auth status fetched and org already connected (existing action) → Success. Auth-status fetch fails → Error (load variant, Retry → Loading). From Empty, Connect with valid credentials → Success; Connect with invalid credentials → Error (Save stays disabled). From Error, re-entering credentials and connecting successfully → Success. From Success, Save (required fields filled) closes the panel and creates/updates the action.
📊 UI state diagram (matches the states and transitions above):
stateDiagram-v2
[*] --> Loading: Panel opened
Loading --> Empty: Auth status fetched — not connected
Loading --> Success: Auth status fetched — already connected
Loading --> Error: Auth-status fetch fails (load variant, Retry shown)
Error --> Loading: Retry (load variant)
Empty --> Success: Connect — valid credentials
Empty --> Error: Connect — invalid credentials
Error --> Success: Re-enter credentials — Connect succeeds
Success --> [*]: Save (required fields filled)
4 Action Types — provisional DynamicFieldsSection per action. Field lists below are a first pass drawn from the public Midtrans API docs to give Engineering a concrete starting contract. They are draft — confirm exact names, types, and required/optional status during RFC (tracked in Open Question #3).
Action 1 — Check Payment Status (GET /v2/{order_id}/status)
| Field | Type | Req? | Source | Notes |
|---|---|---|---|---|
| order_id | string | required | customer message / config | Path param; the Midtrans order_id (or transaction_id) to look up |
No request body — read-only status lookup.
Action 2 — Create Payment Link (POST /v1/payment-links)
| Field | Type | Req? | Source | Notes |
|---|---|---|---|---|
| transaction_details.order_id | string | required | config / conversation | Unique per link |
| transaction_details.gross_amount | integer (IDR) | required | conversation / config | Must equal sum of item_details prices if item_details sent |
| item_details[] | array (id, name, price, quantity) | recommended | config | Line items shown on the payment page |
| customer_details | object (first_name, last_name, email, phone) | optional | conversation | Pre-fills payer info |
| usage_limit | integer | optional | config | Max number of successful payments against the link |
| expiry | object (start_time, duration, unit) | optional | config | Link validity window; defaults to Midtrans account setting if omitted |
| enabled_payments[] | array | optional | config | Restrict to specific payment channels |
Action 3 — Cancel Transaction (POST /v2/{order_id}/cancel)
| Field | Type | Req? | Source | Notes |
|---|---|---|---|---|
| order_id | string | required | customer message / conversation | Path param; transaction to cancel |
No request body — cancel is keyed on the path order_id. Cancellable only from pending/authorize (enforced by the pre-check in §7 #3).
Action 4 — Request Refund (POST /v2/{order_id}/refund)
| Field | Type | Req? | Source | Notes |
|---|---|---|---|---|
| order_id | string | required | customer message / conversation | Path param; transaction to refund |
| refund_key | string | optional (recommended) | system-generated | Idempotency/reference key — see §7 idempotency note |
| amount | integer (IDR) | optional | conversation / config | Omit for full refund; provide for partial refund (if enabled on the merchant account) |
| reason | string | optional | conversation | Free-text refund reason, surfaced in Midtrans dashboard |
7. API & Webhook Behavior
All actions use Authorization: Bearer token via the shipped BOT-4243 midtrans_oauth2 provider. Technical details (HTTP methods, JSON schemas, error codes) resolved during RFC.
| # | Behavior | Entity Affected | Triggered By | Expected Behavior | Failure Behavior |
|---|---|---|---|---|---|
| 1 | Check Payment Status | Payment record (read-only) | AI Agent identifies payment status intent | Calls GET /v2/{order_id}/status. Returns status (settlement/pending/deny/cancel/expire/failure) in conversation. Logs midtrans_action_success. | Timeout >60s: error message + log timeout. Invalid order_id (404): "Payment not found." Auth failure: refresh attempted; if fails, log auth_error. |
| 2 | Create Payment Link | Payment link (created in Midtrans) | AI Agent identifies payment/purchase intent | Calls POST /v1/payment-links. Returns URL as WhatsApp CTA-URL button (WhatsApp) or clickable link (other channels). Logs midtrans_payment_link_created. | Timeout: error message + log timeout. Invalid fields (400): "Unable to create payment link." Auth failure: same pattern. |
| 3 | Cancel Transaction | Transaction status (changed to cancelled) | AI Agent receives explicit customer confirmation | Checks status first (GET /v2/{order_id}/status). If cancellable (pending/authorize): shows confirmation prompt. On confirm: calls POST /v2/{order_id}/cancel. | Not cancellable: "Cannot cancel — current status: [status]." Cancel endpoint NOT called. Customer declines: "Not cancelled." Timeout: error + log. Auth failure: abort + log. |
| 4 | Request Refund | Transaction (refund initiated) | AI Agent receives explicit customer confirmation | Checks eligibility first. If settlement: shows confirmation prompt. On confirm: calls POST /v2/{order_id}/refund. | Not eligible: "Not eligible for refund — status: [status]." Refund NOT called. Customer declines: "No refund requested." Timeout: error + log. Auth failure: abort + log. |
Idempotency, rate limits & partial refunds (to resolve during RFC):
- Idempotency on mutating calls (cancel #3, refund #4). A flaky WhatsApp connection can produce a duplicated customer "yes", risking a double cancel/refund POST. Cancel is naturally idempotent on Midtrans (a second cancel on an already-cancelled order is rejected by the status pre-check). Refund is not — RFC must define a
refund_key(idempotency key) per refund attempt and a short in-conversation dedupe window so one confirmation maps to exactly one refund POST. - Rate limits. Midtrans enforces per-endpoint rate limits. RFC to define backoff/retry behavior and the user-facing message when a call is rate-limited (distinct from the generic timeout copy).
- Partial vs. full refund. Refund supports an optional
amount(full when omitted, partial when provided). RFC to confirm whether partial refunds are in scope for P0; if so, S05 needs an AC for the partial-amount path. Default assumption for P0: full refund only unless RFC decides otherwise.
8. System Flow + User Stories + ACs
8.1 System Flow
Flow: Midtrans action setup — User Journey (Admin configures action; steps 1–7)
- Admin navigates to Bot & Automation → Actions
- Admin creates new action, selects Midtrans action type
- Config panel opens with Action name, Trigger description, auth block (Not connected), disabled dynamic fields
- Admin enters credentials → clicks Connect
- System validates credentials. Valid → "Midtrans connected [env]"; dynamic fields become enabled.
- Failure branch: invalid credentials → "Connection failed. Check your credentials and try again."; Save stays disabled (S01 ERR-1). Admin may re-enter credentials (back to step 4).
- Admin fills dynamic fields → clicks Save
- Failure branch: Save attempted with no established connection → "Midtrans connection required before saving." (S01 ERR-2).
- Action created and appears in Actions list
📊 Setup flow diagram (matches steps 1–7 above):
flowchart TD
A["1. Admin opens Bot & Automation → Actions"] --> B["2. Create new action — select Midtrans action type"]
B --> C["3. Config panel opens — auth block Not connected, dynamic fields disabled"]
C --> D["4. Admin enters credentials → Connect"]
D --> E{"5. Credentials valid?"}
E -- "Valid" --> F["Midtrans connected (env) — dynamic fields enabled"]
E -- "Invalid" --> G["Connection failed message — Save stays disabled (S01 ERR-1)"]
G --> D
F --> H["6. Admin fills dynamic fields → Save"]
H --> I{"Connection established?"}
I -- "Yes" --> J["7. Action created — appears in Actions list"]
I -- "No" --> K["Midtrans connection required before saving (S01 ERR-2)"]
Flow: Midtrans action execution — API Sequence (AI Agent executes in conversation; steps 8–13)
- Customer sends message; AI Agent identifies payment intent (and the order_id where the action requires one; if missing, AI Agent asks the customer for it before any API call — S02 AC-3)
- AI Agent invokes the chatbot BE executor for the configured Midtrans action
- Executor retrieves the cached Bearer token via the BOT-4243 token manager (refreshes if expired/missing)
- Failure branch: token refresh fails → action aborted; error message in conversation;
midtrans_action_failedlogged reason:auth_error(S02 ERR-3, S03 ERR-3, S04 ERR-4, S05 ERR-4)
- Failure branch: token refresh fails → action aborted; error message in conversation;
- For cancel/refund only: executor calls GET /v2/{order_id}/status to validate transaction state; if the state is eligible, AI Agent asks the customer for explicit confirmation
- Failure branch: state not cancellable / not eligible → "Cannot cancel / not eligible — current status: (status)" message; mutating endpoint NOT called (S04 ERR-1, S05 ERR-1)
- Failure branch: customer declines confirmation → "no changes were made" message; mutating endpoint NOT called (S04 ERR-2, S05 ERR-2)
- Executor calls the Midtrans API endpoint for the action (only reached if no failure branch above terminated the flow)
- Failure branch: Midtrans returns 401 (token invalidated despite cache) → executor refreshes the token once via BOT-4243 and retries the call once; if that refresh fails → abort per step 10 failure branch (
auth_error) - Failure branch: no response within 60s → timeout message in conversation;
midtrans_action_failedlogged reason:timeout - Failure branch: Midtrans returns 4xx (invalid order_id / invalid or missing fields) → action-specific error message;
midtrans_action_failedlogged reason:invalid_request
- Failure branch: Midtrans returns 401 (token invalidated despite cache) → executor refreshes the token once via BOT-4243 and retries the call once; if that refresh fails → abort per step 10 failure branch (
- On success: AI Agent delivers result to customer (create_payment_link → CTA-URL button on WhatsApp, clickable link on other channels; other actions → text);
midtrans_action_successlogged
📊 System flow diagram — Flow: Midtrans action execution — API Sequence (matches steps 8–13 above):
sequenceDiagram
participant C as Customer
participant AI as AI Agent
participant EX as Chatbot BE executor
participant M as Midtrans API
C->>AI: Message with payment intent (step 8)
AI->>EX: Invoke configured Midtrans action (step 9)
Note over EX: Step 10 — retrieve cached Bearer token via BOT-4243 token manager (refresh if expired/missing)
alt Token refresh fails (step 10 failure branch)
EX-->>AI: Abort — midtrans_action_failed (auth_error)
AI-->>C: Error message in conversation
else Token available
opt Cancel / Refund only (step 11)
EX->>M: GET /v2/{order_id}/status (state pre-check)
M-->>EX: Transaction status
alt State not cancellable / not eligible
EX-->>AI: Pre-check failed
AI-->>C: Cannot cancel / not eligible — current status (mutating endpoint NOT called)
else State eligible
AI->>C: Explicit confirmation prompt
alt Customer declines
C-->>AI: Decline
AI-->>C: No changes were made (mutating endpoint NOT called)
else Customer confirms
C-->>AI: Confirm — proceed to step 12
end
end
end
EX->>M: Call Midtrans action endpoint (step 12 — only if no branch above terminated)
alt Midtrans returns 401
Note over EX: Refresh token once via BOT-4243, retry call once
alt Retry refresh fails
EX-->>AI: Abort — midtrans_action_failed (auth_error)
AI-->>C: Error message in conversation
else Token refreshed
EX->>M: Retry action endpoint
M-->>EX: Response
end
else No response within 60s
EX-->>AI: midtrans_action_failed (timeout)
AI-->>C: Timeout error message
else Midtrans returns 4xx
EX-->>AI: midtrans_action_failed (invalid_request)
AI-->>C: Action-specific error message
else Success
M-->>EX: Success response
EX-->>AI: Result — midtrans_action_success logged
AI-->>C: Deliver result (step 13 — CTA-URL button on WhatsApp / clickable link or text elsewhere)
end
end
8.2 User Stories
MIDTRANS-S01 — Admin configures a Midtrans native action | Must Have
Story: As an Admin/SPV, I want to configure a Midtrans native action in the AI Agent action builder, so that the AI Agent can execute Midtrans payment operations without requiring manual API configuration.
Before: Admin used the generic API Integration menu requiring manual endpoint/header/body/auth setup. After: Admin selects a Midtrans action type and connects credentials once via a native integration panel.
Data Fields: action_name (string, required), trigger_description (string, required), environment (sandbox/production, required), client_id (string, required), client_secret (string, required — secret), plus the action-specific dynamic fields defined per action type in the DynamicFieldsSection schemas — Action 1–4 tables in §6. New Features. Credential fields match the shipped midtrans_oauth2 auth provider (chatbot/config/auth_providers/midtrans_oauth2.yml:3-6); connection stored per-org in organization_connections (auth_data/tokens/token_cache).
Happy Path:
- AC-1: Given I am an Admin/SPV on the Actions page (eligible plan, flag ON), when I select a Midtrans action type, then the config panel opens with Action name, Trigger description, auth block "Not connected", and disabled dynamic fields.
- AC-2: Given auth block shows "Not connected", when I click Connect and enter valid credentials, then auth block shows "Midtrans connected [environment]" and dynamic fields become enabled.
- AC-3: Given connection established and required fields filled, when I click Save, then action appears in Actions list with correct Midtrans action type label.
- AC-4: Given existing Midtrans action, when I click Re-authorize and enter updated credentials, then connection is refreshed and updated environment is shown.
- AC-5: Given a saved Midtrans action, when an Admin/SPV edits or deletes it from the Actions list, then the change persists and the AI Agent uses the updated config on next invocation; deletion removes the action from the AI Agent's action set (deleting the action does not disconnect the org-level Midtrans credential).
Error Path:
- ERR-1: Given invalid credentials, when I click Connect, then "Connection failed. Check your credentials and try again." Save remains disabled. Event midtrans_action_failed logged reason: auth_error.
- ERR-2: Given connection not established, when I attempt to Save, then "Midtrans connection required before saving."
- ERR-3: Given panel open, when auth status fetch fails on load, then "Failed to load configuration. Try again." + Retry button. Event logged reason: panel_load_error.
Permission Model:
- CAN: Admin, SPV (eligible plan, flag ON)
- CAN edit/delete saved Midtrans actions: Admin, SPV (same lifecycle rights as create — follows the platform's existing action-config pattern)
- CANNOT: CS Agents, read-only users
- CANNOT edit/delete saved Midtrans actions: CS Agent, AI Agent
- Unauthorized: Midtrans action types not rendered in selector for ineligible roles/plans
UI States: Loading (skeleton), Empty (all blank, Not connected), Error (load failed + Retry), Success (connected, fields filled, Save enabled)
MIDTRANS-S02 — AI Agent checks payment status | Must Have
Story: As a customer talking to the Qontak AI Agent, I want to ask about my payment status and receive an immediate answer, so that I don't need to wait for a human agent.
Before: AI Agent couldn't check payment status; CS agent manually navigated to Midtrans dashboard. After: AI Agent calls GET /v2/{order_id}/status and returns status in the conversation in real time.
Data Fields: order_id (string, required — from customer message), bearer_token (string, required — BOT-4243), action_type (check_payment_status, internal)
Happy Path:
- AC-1: Given Check Payment Status action configured (flag ON), when customer asks about status and AI Agent identifies order_id, then AI Agent calls GET /v2/{order_id}/status and returns status in conversation.
- AC-2: Given API call completes within 60s, when Midtrans returns valid status, then AI Agent delivers natural language message. Event midtrans_action_success logged with action_type, order_id, status.
- AC-3: Given customer has not provided order_id, when AI Agent identifies payment status intent, then AI Agent asks customer for order_id before calling API.
Error Path:
- ERR-1: Given check_payment_status called, when no response within 60s, then AI Agent: "I was unable to check your payment status. Please try again or contact support." Event logged reason: timeout.
- ERR-2: Given non-existent order_id, when Midtrans returns 404, then AI Agent: "I couldn't find a payment with that order ID. Please verify and try again."
- ERR-3: Given the Bearer token has expired, when the BOT-4243 token manager fails to refresh it, then an error message is shown in the conversation and the event is logged with reason: auth_error.
Permission Model: CAN: AI Agent (configured, flag ON, eligible plan). CANNOT: Human CS agents (no manual trigger). Unauthorized: Not executed if flag OFF or action not configured.
UI States: Loading (typing indicator ≤60s), Empty (N/A — AI Agent prompts for order_id), Error (error message in conversation), Success (status delivered in conversation)
MIDTRANS-S03 — AI Agent creates a payment link | Must Have
Story: As a customer talking to the Qontak AI Agent, I want to receive a payment link directly in our conversation, so that I can complete my purchase immediately without leaving the chat.
Before: CS agent manually created payment link in Midtrans, pasted as plain text. After: AI Agent calls POST /v1/payment-links and delivers URL as WhatsApp CTA-URL button (WhatsApp) or clickable link (other channels) — no human involvement.
Data Fields: transaction fields per the Action 2 — Create Payment Link DynamicFieldsSection schema in §6. New Features (transaction_details.order_id + transaction_details.gross_amount required; item_details, customer_details, usage_limit, expiry, enabled_payments per that table), bearer_token (required — BOT-4243), channel_type (whatsapp/other — determines link delivery format)
Happy Path:
- AC-1: Given Create Payment Link action configured (flag ON), when AI Agent identifies payment intent and all required fields available, then AI Agent calls POST /v1/payment-links using Bearer token.
- AC-2: Given valid URL returned within 60s, when channel is WhatsApp, then AI Agent sends URL as WhatsApp CTA-URL button (not plain text). Event midtrans_payment_link_created logged channel_type: whatsapp.
- AC-3: Given valid URL returned, when channel is NOT WhatsApp, then AI Agent sends URL as clickable link. Event logged with appropriate channel_type.
Error Path:
- ERR-1: Given create_payment_link called, when no response within 60s, then "I was unable to generate a payment link. Please try again or contact support." Event logged reason: timeout.
- ERR-2: Given create_payment_link has been called, when Midtrans returns 400 (invalid/missing fields), then "I was unable to create a payment link. Please contact support." Event logged reason: invalid_request.
- ERR-3: Given the Bearer token has expired, when the BOT-4243 refresh attempt fails, then the action is aborted and the event is logged with reason: auth_error.
Permission Model: CAN: AI Agent (configured, flag ON, eligible plan). CANNOT: Human CS agents (no manual trigger). CANNOT: Payment links cannot be revoked via this action once created. Link expiry governed by Midtrans account settings. Unauthorized: Not executed if flag OFF.
UI States: Loading (typing indicator ≤60s), Empty (N/A — fields must be present), Error (error message in conversation), Success (CTA-URL button on WhatsApp / clickable link on other channels)
MIDTRANS-S04 — AI Agent cancels a transaction (with confirmation) | Must Have
Story: As a customer talking to the Qontak AI Agent, I want to cancel my transaction through the chat, so that I can reverse my order without waiting for a human agent.
Before: CS agent manually processed cancellation in Midtrans dashboard. After: AI Agent validates cancellability, requests customer confirmation, then calls POST /v2/{order_id}/cancel — fully automated.
Data Fields: order_id (required — from customer), transaction_status (internal — retrieved via status check), customer_confirmation (boolean, required), bearer_token (required — BOT-4243)
Happy Path:
- AC-1: Given Cancel Transaction action configured (flag ON), when AI Agent identifies cancellation request with order_id, then AI Agent calls GET /v2/{order_id}/status first to check if transaction is cancellable (pending or authorize).
- AC-2: Given transaction is cancellable, when status returns pending/authorize, then AI Agent presents: "Are you sure you want to cancel order [order_id]? This action cannot be undone." before calling cancel endpoint.
- AC-3: Given customer confirms, when POST /v2/{order_id}/cancel returns success, then AI Agent responds: "Your order [order_id] has been successfully cancelled." Event midtrans_action_success logged.
Error Path:
- ERR-1: Given transaction NOT cancellable (settlement/expire/cancel/deny/failure), when AI Agent checks status, then "This transaction cannot be cancelled — current status: [status]." Cancel endpoint NOT called.
- ERR-2: Given the cancellation confirmation prompt is shown, when the customer declines, then "Understood. Your order has not been cancelled — no changes were made." The cancel endpoint is not called.
- ERR-3: Given customer confirms, when cancel API no response within 60s, then "Unable to cancel your transaction. Please try again or contact support." Event logged reason: timeout.
- ERR-4: Given the Bearer token has expired, when the BOT-4243 refresh attempt fails, then the action is aborted and the event is logged with reason: auth_error.
Permission Model: CAN: AI Agent (configured, flag ON). CANNOT: Human CS agents. Unauthorized: Not executed if flag OFF.
UI States: Loading (typing indicator during status check + cancel call ≤60s), Empty (AI Agent prompts for order_id), Error (error in conversation), Success (cancellation confirmed in conversation)
MIDTRANS-S05 — AI Agent requests a refund (with confirmation) | Must Have
Story: As a customer talking to the Qontak AI Agent, I want to request a refund for my order through the chat, so that I can get my money back without waiting for a human agent.
Before: CS agent manually processed refund in Midtrans dashboard. After: AI Agent validates refund eligibility, requests customer confirmation, then calls POST /v2/{order_id}/refund — fully automated.
Data Fields: order_id (required — from customer), transaction_status (internal — retrieved via status check), customer_confirmation (boolean, required), refund fields per the Action 4 — Request Refund DynamicFieldsSection schema in §6. New Features (refund_key system-generated idempotency key, amount, reason — per that table and the §7 idempotency note), bearer_token (required — BOT-4243)
Happy Path:
- AC-1: Given Request Refund action configured (flag ON), when AI Agent identifies refund request with order_id, then AI Agent calls GET /v2/{order_id}/status first to check eligibility (settlement status required).
- AC-2: Given transaction eligible (settlement), when eligibility check passes, then AI Agent presents: "Are you sure you want to request a refund for order [order_id]? This action cannot be undone." before calling refund endpoint.
- AC-3: Given customer confirms, when POST /v2/{order_id}/refund returns success, then AI Agent: "Your refund request for order [order_id] has been submitted successfully." Event midtrans_action_success logged.
Error Path:
- ERR-1: Given transaction NOT eligible (pending/cancel/expire/deny/failure/already refunded), when AI Agent checks status, then "This transaction is not eligible for a refund — current status: [status]." Refund endpoint NOT called.
- ERR-2: Given the refund confirmation prompt is shown, when the customer declines, then "Understood. No refund has been requested — no changes were made." The refund endpoint is not called.
- ERR-3: Given customer confirms, when refund API no response within 60s, then "Unable to process your refund request. Please try again or contact support." Event logged reason: timeout.
- ERR-4: Given the Bearer token has expired, when the BOT-4243 refresh attempt fails, then the action is aborted and the event is logged with reason: auth_error.
Permission Model: CAN: AI Agent (configured, flag ON). CANNOT: Human CS agents. Unauthorized: Not executed if flag OFF.
UI States: Loading (typing indicator during eligibility check + refund call ≤60s), Empty (AI Agent prompts for order_id), Error (error in conversation), Success (refund submission confirmed in conversation)
Negative Scenarios
- NEG-1: Given Admin/SPV tries to add second Midtrans account while one connected, when they view auth block, then no "add another account" option — only Re-authorize and Disconnect shown.
- NEG-2: Given human CS agent views conversation where AI Agent executed Midtrans action, when they look for manual re-trigger, then no trigger control rendered — action results are read-only in this phase.
- NEG-3: Given Admin/SPV in action type selector, when they search for P1/P2 Midtrans actions (e.g., Create Invoice, Create Subscription), then these types are not available in this phase.
- NEG-4: Given Admin/SPV using Qontak mobile app, when they navigate to Bot & Automation settings, then AI Agent Actions configuration menu is not rendered — Midtrans action setup requires web (AI Agent config: Web only).
9. Rollout
- Feature flag: TBD | default: OFF. Enabled per account after connecting Midtrans credentials.
- Stage 1: Internal QA (BOT squad sandbox accounts)
- GA: All Sales Suite Plus, Ultimate, Qontak 360 on request
- Backward compat: Yes — no existing behavior modified. Net-new feature.
- Migration: None required.
10. Observability
| Event Name | Trigger | Properties |
|---|---|---|
| midtrans_action_executed | AI Agent initiates any Midtrans action | action_type, order_id (if applicable), cid, conversation_id, timestamp |
| midtrans_action_success | Midtrans API returns successful response | action_type, order_id, response_status, duration_ms, cid, conversation_id |
| midtrans_action_failed | Midtrans API fails, times out, or auth error | action_type, order_id (if applicable), failure_reason (timeout/auth_error/invalid_request/api_error), cid, conversation_id |
| midtrans_payment_link_created | Successful create_payment_link; URL delivered | payment_link_url, channel_type, cid, conversation_id |
| midtrans_auth_token_refreshed | Bearer token refresh triggered by BOT-4243 | environment, duration_ms, cache_hit (true/false), cid |
Dashboard owner: BOT Squad
Alerts:
- midtrans_action_failed rate > 5% in any 15-min window → Slack: BOT squad alert channel
- midtrans_auth_token_refreshed failure (3 consecutive for same cid) → Slack + escalation to Eng Lead
Post-Launch Monitoring Cadence: TBD (target: monthly). Owner: Dimas Fauzi Hidayat (BOT Squad PM). Investigation triggered by alert firing, not scheduled threshold. Rollback: if midtrans_action_failed > 10% sustained for 1 hour and unresolvable within 2 hours, PM disables flag globally.
11. Success Metrics
⭐ Primary KPI: Number of CIDs actively using at least one Midtrans action in production
- Definition: Unique accounts on eligible plans that configured and triggered at least one P0 action via AI Agent in the past 30 days
- Baseline: N/A (new feature)
- Target: ≥5 CIDs within 90 days of GA
Quality: AI Agent Midtrans action success rate
- Definition: % of midtrans_action_executed resulting in midtrans_action_success (all 4 P0 actions combined)
- Baseline: N/A
- Target: ≥95% within 60 days of GA
12. Launch Plan & Stage Gates
| Stage | Audience | Duration | Success Gate | Owner |
|---|---|---|---|---|
| Internal QA | BOT squad internal test accounts (sandbox) | 2 weeks | QA AI Agent testing passed for all 4 P0 actions. Zero P0/P1 bugs. Confirmation flow (cancel/refund) verified. Auth token lifecycle verified. | PM + QA |
| Closed Beta | 3–5 Production accounts (Sales Suite Plus or Ultimate, manually enabled) | 3 weeks | Success rate ≥95% for 1 consecutive week in production. Zero P0 bugs. Auth token refresh verified under real credentials. No critical UX issues from beta accounts. | PM + CSM |
| GA | All Sales Suite Plus, Ultimate, Qontak 360 | Ongoing | Closed Beta gates sustained for 2 consecutive weeks. Success rate ≥95%. Zero P0 bugs. PMM launch comms sent. | PM + BOT Squad |
13. Dependencies
| Dependency | Owning Team | Deliverable Needed | Blocking? |
|---|---|---|---|
| BOT-4243 — Midtrans OAuth Authentication | BOT Squad | SHIPPED (verified chatbot@master 2026-07-03): midtrans_oauth2 auth_type (chatbot/config/auth_providers/midtrans_oauth2.yml:1-6; auth_type_enum in app/models/organization_connection.rb:24) with Redis-cached token manager, ttl_seconds: 3300 (~55 min). Fields: environment (sandbox/production), client_id, client_secret. Token endpoint (oauth2 client credentials): POST https://api.sandbox.midtrans.com/v1/oauth/token (sandbox) / https://api.midtrans.com/v1/oauth/token (production). Credentials stored in organization_connections (auth_data/tokens/token_cache columns). Scope of what shipped: auth provider config + enum + storage only — the 4 native action types, FE config panel, and observability events in this PRD are still to-build. | NO — Done |
| Midtrans sandbox environment access | Midtrans / PM | Sandbox API credentials for QA testing of all 4 P0 actions | NO — obtain in parallel with development |
14. Key Decisions + Alternatives Rejected
14a — Decisions Made
| Date | Decision | Rationale |
|---|---|---|
| 2026-06-12 | Build as native integration, not generic API connector | Generic API Integration menu requires manual endpoint/header/body/auth config — too technical for Admin/SPV. Native integration eliminates this friction entirely. |
| 2026-06-12 | All 4 P0 actions in one PRD | Actions share BOT-4243 auth architecture and 2 mandatory base fields. Separating them adds overhead without benefit. |
| 2026-06-12 | create_payment_link sends URL as WhatsApp CTA-URL button, not plain text | CTA-URL buttons are Qontak's primary WhatsApp USP — higher conversion. Non-WhatsApp channels receive a clickable link. |
| 2026-06-12 | ANCHOR + NEW PRD structure for Midtrans initiative | 15 total actions across 3 phases (P0/P1/P2) warrant ANCHOR to track initiative history and phase dependencies. |
| 2026-06-12 | cancel_transaction and request_refund require AI Agent to check transaction state before calling API | Prevents API calls on ineligible transactions, reducing unnecessary calls and improving error quality. |
| 2026-06-12 | cancel_transaction and request_refund require explicit customer confirmation before mutating API call | Cancellation and refund are irreversible. Confirmation prevents accidental execution — standard practice for destructive actions in conversational AI. |
| 2026-06-12 | UX pattern follows Google Calendar native integration | Consistent with existing pattern familiar to Admin/SPV users: connect once → actions appear in agent builder. |
14b — Alternatives Rejected
| Alternative | Why Rejected | Date |
|---|---|---|
| Generic API Integration menu for Midtrans actions | Requires manual endpoint/header/body/auth config per action — defeats native integration value prop and blocks non-developer admins | 2026-06-12 |
| Separate PRDs per P0 action | Over-engineered given shared auth architecture. Increases PM and eng overhead without improving spec quality. | 2026-06-12 |
| WhatsApp-only channel restriction for create_payment_link | Creates inconsistent AI Agent behavior across channels. All channels should return a usable link. | 2026-06-12 |
| Skip confirmation step for cancel/refund | Higher risk of accidental irreversible actions in conversational context. Low-friction confirmation is standard practice. | 2026-06-12 |
15. Open Questions
| # | Type | Question | Owner | Deadline |
|---|---|---|---|---|
| 1 | Open Question | Feature flag name — TBD, needs engineering alignment | BOT Squad Eng Lead | 2026-07-17 |
| 2 | Open Question | RFC link — planned, not yet created with engineering team | Dimas / BOT Squad | 2026-07-17 |
| 3 | Open Question | Confirm/finalize the provisional dynamic field lists in S6 (names, types, required vs optional) against Midtrans API docs during RFC | BOT Squad Eng Lead | 2026-07-17 |
| 6 | Open Question | Refund idempotency (refund_key + dedupe window), rate-limit/backoff behavior, and partial-vs-full refund scope for P0 — see S7 idempotency note | BOT Squad Eng Lead | 2026-07-17 |
| 4 | Open Question | Midtrans refund SLA — needed for S05 AC-3 message copy | Dimas / Midtrans docs | 2026-07-17 |
| 5 | Open Question | Post-launch monitoring cadence formal thresholds — TBD | Dimas / BOT Squad | 2026-07-30 |
| 7 | Risk | Double refund if request_refund fires twice (duplicate function-call — e.g. a duplicated customer "yes" on a flaky channel triggers two refund POSTs). Mitigation: refund_key idempotency key per refund attempt + in-conversation dedupe window per the §7 idempotency note (definition tracked in OQ #6), with full-refund-only P0 default; must land in the RFC before Closed Beta. | Dimas | 2026-07-17 |
PRD CHANGELOG
| Version | Date | By | Section | Type | Summary |
|---|---|---|---|---|---|
| 1.0 | 2026-06-12 | Claude | All | CREATED | Initial NEW PRD (Phase 1: P0 Core Actions) generated from grooming session. Covers 4 P0 actions: check_payment_status, create_payment_link, cancel_transaction, request_refund. ANCHOR PRD created alongside. |
| 1.1 | 2026-06-12 | Claude | S8, S12, S15 | MODIFIED | Applied AI-readiness scoring fixes: S03 payment link lifecycle CANNOT; NEG-4 web-only constraint; S05 Test Coverage Matrix; S12 Closed Beta stage + durations; S15 calendar dates. |
| 1.2 | 2026-06-15 | Claude | S6, S7, S15 | MODIFIED | Post-score-prd improvements: (1) S6 — added provisional DynamicFieldsSection field schemas for all 4 actions (draft, confirm in RFC). (2) S7 — added idempotency (refund_key + dedupe window), rate-limit, and partial-vs-full refund note. (3) S15 — reworded OQ#3, added OQ#6 (refund idempotency/rate-limit/partial scope). |
| 1.3 | 2026-07-03 | Claude (ground-prd sweep) | S5, S6, S7, S8 (MIDTRANS-S01), S13 | UPDATED | Corrected BOT-4243 shipped reality against chatbot@master: auth_type is midtrans_oauth2 (not midtrans_oauth), credential fields are environment/client_id/client_secret only (server_key/client_key/partner_id/merchant_id removed), Redis token TTL is 3300s (~55 min, not ~15 min), token endpoint is /v1/oauth/token per environment. S13 scoped "Done" to what actually shipped (provider config + enum + org_connections storage). |
| 1.4 | 2026-07-03 | Claude (score-readiness pass) | Header, S1, S6, S8, S15 | UPDATED | Score-readiness fixes per v3.3 report: S8 flows named + typed (Setup — User Journey; Execution — API Sequence) with failure branches at decision points, setup flowchart + execution mermaid sequence diagram (incl. token-refresh-on-401) added; MIDTRANS-S01 AC-5 (edit/delete saved action) + Permission Model lifecycle lines — defaulted decision for PM review: Admin/SPV CAN edit/delete saved Midtrans actions (follows the platform's existing action-config pattern); CS Agent/AI Agent CANNOT; completed Given/When/Then on S02 ERR-3, S03 ERR-2/ERR-3, S04 ERR-2/ERR-4, S05 ERR-2/ERR-4; anchored [transaction_fields]/[refund_fields]/[action_dynamic_fields] placeholders to the §6 DynamicFieldsSection schemas; reconciled Figma statement in Header + S6 (net-new MidtransActionConfigPanel inside the existing action drawer; frame pending, RFC to confirm); S6 component-tree purpose annotations + state-transition prose + UI state diagram; S1 one-liner tightened ≤25 words (WHAT + FOR WHOM) + concrete qualitative cost (no invented metrics; evidence gap declared); S15 overdue OQs #1–#4, #6 re-dated to 2026-07-17 (owners kept, no answers invented) + new Risk row #7 (double refund on duplicate function-call) with refund_key/dedupe mitigation. Header Last Updated → 2026-07-03. |