Skip to main content

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

FieldValue
PMDimas Fauzi Hidayat
PRD Version1.4
StatusDRAFT
PRD TypeNEW
EpicBOT-4340
SquadBOT
RFC LinkPlanned — RFC to be created and linked once aligned with engineering team
Figma MasterPending — 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.
AnchorNative Integration — ANCHOR — Midtrans provider (Confluence)
Labelsepic:chatbot-ai | module:ai-agent | feature:midtrans-native-integration
Last Updated2026-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

PersonaRoleGoalPainWorkaround
Primary — Admin/SPVQontak admin or supervisor responsible for configuring AI Agent flows on behalf of their companyConfigure Midtrans payment actions in the AI Agent without developer involvementGeneric API Integration action menu requires manual URL, header, body, and auth configuration — too technical for non-developer adminsUse the API Integration action menu with developer assistance, or escalate payment queries entirely to human CS agents

4. Non-Goals

  1. Midtrans dashboard UI embedded in Qontak — no in-app transaction management view in this phase.
  2. Support for multiple Midtrans accounts per workspace — single account connection only.
  3. Manual trigger by human agents — all P0 actions are AI Agent-initiated only in this phase.
  4. P1 billing & subscription actions (create_snap_token, create_invoice, get_invoice, void_invoice, create_subscription, get_subscription, cancel_subscription) — deferred to Phase 2.
  5. 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_oauth2 auth_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_oauth2 provider 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)

FieldTypeReq?SourceNotes
order_idstringrequiredcustomer message / configPath 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)

FieldTypeReq?SourceNotes
transaction_details.order_idstringrequiredconfig / conversationUnique per link
transaction_details.gross_amountinteger (IDR)requiredconversation / configMust equal sum of item_details prices if item_details sent
item_details[]array (id, name, price, quantity)recommendedconfigLine items shown on the payment page
customer_detailsobject (first_name, last_name, email, phone)optionalconversationPre-fills payer info
usage_limitintegeroptionalconfigMax number of successful payments against the link
expiryobject (start_time, duration, unit)optionalconfigLink validity window; defaults to Midtrans account setting if omitted
enabled_payments[]arrayoptionalconfigRestrict to specific payment channels

Action 3 — Cancel Transaction (POST /v2/{order_id}/cancel)

FieldTypeReq?SourceNotes
order_idstringrequiredcustomer message / conversationPath 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)

FieldTypeReq?SourceNotes
order_idstringrequiredcustomer message / conversationPath param; transaction to refund
refund_keystringoptional (recommended)system-generatedIdempotency/reference key — see §7 idempotency note
amountinteger (IDR)optionalconversation / configOmit for full refund; provide for partial refund (if enabled on the merchant account)
reasonstringoptionalconversationFree-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.

#BehaviorEntity AffectedTriggered ByExpected BehaviorFailure Behavior
1Check Payment StatusPayment record (read-only)AI Agent identifies payment status intentCalls 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.
2Create Payment LinkPayment link (created in Midtrans)AI Agent identifies payment/purchase intentCalls 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.
3Cancel TransactionTransaction status (changed to cancelled)AI Agent receives explicit customer confirmationChecks 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.
4Request RefundTransaction (refund initiated)AI Agent receives explicit customer confirmationChecks 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)

  1. Admin navigates to Bot & Automation → Actions
  2. Admin creates new action, selects Midtrans action type
  3. Config panel opens with Action name, Trigger description, auth block (Not connected), disabled dynamic fields
  4. Admin enters credentials → clicks Connect
  5. 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).
  6. Admin fills dynamic fields → clicks Save
    • Failure branch: Save attempted with no established connection → "Midtrans connection required before saving." (S01 ERR-2).
  7. 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)

  1. 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)
  2. AI Agent invokes the chatbot BE executor for the configured Midtrans action
  3. 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_failed logged reason: auth_error (S02 ERR-3, S03 ERR-3, S04 ERR-4, S05 ERR-4)
  4. 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)
  5. 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_failed logged reason: timeout
    • Failure branch: Midtrans returns 4xx (invalid order_id / invalid or missing fields) → action-specific error message; midtrans_action_failed logged reason: invalid_request
  6. 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_success logged

📊 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)


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 NameTriggerProperties
midtrans_action_executedAI Agent initiates any Midtrans actionaction_type, order_id (if applicable), cid, conversation_id, timestamp
midtrans_action_successMidtrans API returns successful responseaction_type, order_id, response_status, duration_ms, cid, conversation_id
midtrans_action_failedMidtrans API fails, times out, or auth erroraction_type, order_id (if applicable), failure_reason (timeout/auth_error/invalid_request/api_error), cid, conversation_id
midtrans_payment_link_createdSuccessful create_payment_link; URL deliveredpayment_link_url, channel_type, cid, conversation_id
midtrans_auth_token_refreshedBearer token refresh triggered by BOT-4243environment, 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

StageAudienceDurationSuccess GateOwner
Internal QABOT squad internal test accounts (sandbox)2 weeksQA 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 Beta3–5 Production accounts (Sales Suite Plus or Ultimate, manually enabled)3 weeksSuccess 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
GAAll Sales Suite Plus, Ultimate, Qontak 360OngoingClosed Beta gates sustained for 2 consecutive weeks. Success rate ≥95%. Zero P0 bugs. PMM launch comms sent.PM + BOT Squad

13. Dependencies

DependencyOwning TeamDeliverable NeededBlocking?
BOT-4243 — Midtrans OAuth AuthenticationBOT SquadSHIPPED (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 accessMidtrans / PMSandbox API credentials for QA testing of all 4 P0 actionsNO — obtain in parallel with development

14. Key Decisions + Alternatives Rejected

14a — Decisions Made

DateDecisionRationale
2026-06-12Build as native integration, not generic API connectorGeneric API Integration menu requires manual endpoint/header/body/auth config — too technical for Admin/SPV. Native integration eliminates this friction entirely.
2026-06-12All 4 P0 actions in one PRDActions share BOT-4243 auth architecture and 2 mandatory base fields. Separating them adds overhead without benefit.
2026-06-12create_payment_link sends URL as WhatsApp CTA-URL button, not plain textCTA-URL buttons are Qontak's primary WhatsApp USP — higher conversion. Non-WhatsApp channels receive a clickable link.
2026-06-12ANCHOR + NEW PRD structure for Midtrans initiative15 total actions across 3 phases (P0/P1/P2) warrant ANCHOR to track initiative history and phase dependencies.
2026-06-12cancel_transaction and request_refund require AI Agent to check transaction state before calling APIPrevents API calls on ineligible transactions, reducing unnecessary calls and improving error quality.
2026-06-12cancel_transaction and request_refund require explicit customer confirmation before mutating API callCancellation and refund are irreversible. Confirmation prevents accidental execution — standard practice for destructive actions in conversational AI.
2026-06-12UX pattern follows Google Calendar native integrationConsistent with existing pattern familiar to Admin/SPV users: connect once → actions appear in agent builder.

14b — Alternatives Rejected

AlternativeWhy RejectedDate
Generic API Integration menu for Midtrans actionsRequires manual endpoint/header/body/auth config per action — defeats native integration value prop and blocks non-developer admins2026-06-12
Separate PRDs per P0 actionOver-engineered given shared auth architecture. Increases PM and eng overhead without improving spec quality.2026-06-12
WhatsApp-only channel restriction for create_payment_linkCreates inconsistent AI Agent behavior across channels. All channels should return a usable link.2026-06-12
Skip confirmation step for cancel/refundHigher risk of accidental irreversible actions in conversational context. Low-friction confirmation is standard practice.2026-06-12

15. Open Questions

#TypeQuestionOwnerDeadline
1Open QuestionFeature flag name — TBD, needs engineering alignmentBOT Squad Eng Lead2026-07-17
2Open QuestionRFC link — planned, not yet created with engineering teamDimas / BOT Squad2026-07-17
3Open QuestionConfirm/finalize the provisional dynamic field lists in S6 (names, types, required vs optional) against Midtrans API docs during RFCBOT Squad Eng Lead2026-07-17
6Open QuestionRefund idempotency (refund_key + dedupe window), rate-limit/backoff behavior, and partial-vs-full refund scope for P0 — see S7 idempotency noteBOT Squad Eng Lead2026-07-17
4Open QuestionMidtrans refund SLA — needed for S05 AC-3 message copyDimas / Midtrans docs2026-07-17
5Open QuestionPost-launch monitoring cadence formal thresholds — TBDDimas / BOT Squad2026-07-30
7RiskDouble 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.Dimas2026-07-17

PRD CHANGELOG

VersionDateBySectionTypeSummary
1.02026-06-12ClaudeAllCREATEDInitial 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.12026-06-12ClaudeS8, S12, S15MODIFIEDApplied 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.22026-06-15ClaudeS6, S7, S15MODIFIEDPost-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.32026-07-03Claude (ground-prd sweep)S5, S6, S7, S8 (MIDTRANS-S01), S13UPDATEDCorrected 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.42026-07-03Claude (score-readiness pass)Header, S1, S6, S8, S15UPDATEDScore-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.