Skip to main content

WhatsApp Campaign | MM Dynamic Pricing — Phase 1: Bid-Based Messaging

HEADER BLOCK

FieldValue
PMEvelin Suwantio
PRD Version1.0
StatusDRAFT
PRD TypeNEW
EpicQC-20757
SquadRevenue Squad
RFC LinkIn progress — not yet published
Figma MasterN/A — pending design. Reference: "New Pricing feature - Marketing Messages | Suggested UI changes (1).pdf" attached on the source Confluence page
AnchorNo — standalone, single-squad
Labelsepic:whatsapp-campaign-dynamic-pricing | module:wa-campaign | feature:bid-based-messaging
Last Updated2026-07-03

Source: Reformatted from the legacy Confluence draft WhatsApp Campaign: MM Dynamic Pricing (Bid-Based Messaging) - Draft. Eng 1-Pager: Confluence. Meta pricing reference: Official Dev Doc.

✅ Reformat Complete — No Flags

All mandatory sections were filled during the coaching interview. This PRD meets the same standard as a write-prd output. No outstanding GAP FLAGS remain — the residual unknowns are captured as tracked Risk rows in Section 16 (Open Questions), each with a mitigation path.

Table of Contents

2. One-liner + Problem

One-liner: Enable Campaign Admins at MM API companies to set a maximum bid price for WhatsApp marketing messages, replacing Meta's fixed per-message pricing.

Problem: Meta is replacing WhatsApp marketing messages' fixed per-message pricing with a dynamic bid-based model, where a message is only delivered if the business's bid meets or exceeds Meta's reserved price for that recipient. Campaign Admins at MM API companies today have no way to control this trade-off — they either overpay for low-demand recipients or lose delivery entirely to high-demand recipients, with no visibility into why a message didn't land. Without a bid-strategy mechanism in place before Meta's model goes live, businesses risk degraded delivery rates and unpredictable WhatsApp Balance spend.

3. Target Users + Persona Context

PersonaRoleGoalPainWorkaround
Primary — Campaign AdminMarketing/Ops staff at MM API companies who create and send WA marketing campaignsMaximize message delivery to customers while controlling per-message spendUnder fixed pricing, either overpays for low-demand recipients or can't reach high-demand recipients at all — no lever to control this trade-off, and no visibility into why specific recipients don't get deliveredAccepts one flat price for the whole list; over-provisions WA Balance as a buffer against running out mid-campaign; manually cross-references delivery reports to guess which recipients failed and why

(See Constraints for plan availability and feature flag scope.)

4. Non-Goals

  1. Dynamic bid-based pricing does not apply to WhatsApp Cloud API — MM API only.
  2. Sending marketing messages via Followup type in Inbox is not covered — always uses Default Price.
  3. Campaigns scheduled via "Send recurring" before this feature's release default to the Default Price bidding option and cannot retry failed recurring sends.
  4. Manual, per-recipient bid amounts are not supported — the bid amount applies uniformly to the entire recipient list.
  5. Retry is capped at 3 total attempts (1 automatic + 2 manual) — unlimited or indefinite retry is out of scope.
  6. Country-specific bid floors/ceilings follow Meta's published per-country marketing price automatically — manual admin override of these bounds is out of scope.
  7. Real-time per-message invoice reconciliation is out of scope — reconciliation runs on a monthly cadence against Meta's invoice.

Scope Changes

Engineering surfaces this PRD touches (controlled vocab). Kept in sync with the scope_changes frontmatter above.

  • Backend — bid strategy storage on campaigns; local bid-too-low detection heuristic (Meta returns no dedicated error code); retry orchestration (1 automatic + 2 manual, max 3 attempts); WA Balance deduction by reserved price (from delivered/read webhook) with monthly reconciliation against Meta's invoice.
  • Frontend — Bid Strategy selector step in campaign creation (Send now/Send later); retry banner + manual bid input on Campaign Detail; Bid Information and Bid Performance Insight panels on Campaign Detail.
  • Design — Figma for the bid strategy selector and the Campaign Detail additions (currently TBD — see Header Block).

5. Constraints

FieldValue
PlatformWeb only (Qontak dashboard)
PerformanceBid strategy price preview must render ≤2s during campaign creation
Data limitsBid amount bounds derived from Meta's published per-country marketing-message price: minimum = published price for that country (e.g. Rp300 for Indonesia); maximum = 2× that published price. Bounds are per recipient's country, not a fixed universal value.
Plan scopeMM API companies only (WA Cloud API companies excluded — see Non-Goals)
Feature flagwa_dynamic_pricing_bid_strategy | default: OFF, enabled per company_id
Read/writeCampaign Admin: read + write bid strategy/amount. All other roles: read-only on Campaign Detail's Bid Information panel.

6. Feature Changes

Change ID: CHG-001

FieldDetail
Change TypeModified page — Campaign Detail; Modified backend behavior — WA Balance deduction
Page/campaigns/{id}/detail
Page IntentCampaign Admin reviews delivery status, cost, and can recover failed recipients
Before• WA Balance is deducted a fixed per-message price regardless of demand.
• Campaign Detail shows delivery status per recipient with no bid/price context.
After• WA Balance is deducted by Meta's reserved price (reported via delivered/read webhook), reconciled monthly against Meta's invoice.
• Campaign Detail additionally shows a Bid Information panel, Bid Performance Insight, a retry banner for low-bid failures, and a retried-recipient icon per row.
ElementBeforeAfter
Balance deductionFixed price per messageReserved price per message (webhook-reported, reconciled monthly vs. invoice)
Campaign DetailNo bid/price infoBid Information + Bid Performance Insight + retry banner + retried-recipient icon

Figma: Pending — see Stitch Prompts section (Frame-level link — see Header.)

7. New Features

Feature: Bid Strategy Selector (new step inside WA Campaign creation, "Send now"/"Send later" only)

FieldDetail
URL/campaigns/create (existing page, new section)
AccessCampaign Admin role, for companies with MM API active

Component Tree:

ComponentParentPurpose
BidStrategyStepNew step in the campaign creation flow
BidStrategyRadioGroupBidStrategyStepPresents Cost Saver / Default / Aggressive / Custom, each with an indicative price
CustomBidAmountInputBidStrategyRadioGroupShown only when Custom is selected; enforces per-country min/max
BidPriceExplainerTooltipBidStrategyStepExplains "you'll be charged the Reserved Price, not your Bid Amount"

UI States:

StateDescription
EmptyNo historical price yet for this recipient list → Cost Saver/Aggressive show static defaults (±10% off/on the Default price, e.g. Rp536.69 / Rp655.96 for a Rp596.33 Indonesia example)
LoadingSkeleton while the system fetches historical reserved-price data to compute strategy prices
ErrorPrice-fetch fails → only "Default" is shown; Cost Saver/Aggressive disabled with tooltip "Pricing unavailable — try again later"
SuccessAll 4 strategies shown with computed indicative prices

Figma: Pending — see Stitch Prompts section


Feature: Retry Failed Messages panel (Campaign Detail)

FieldDetail
URL/campaigns/{id}/detail
AccessCampaign Admin who owns the campaign, MM API companies only

Component Tree:

ComponentParentPurpose
RetryBidBannerCampaignDetailPageShown when failed-due-to-low-bid recipients exist
RecommendedBidAmountRetryBidBannerDisplays the system-recommended (highest known) reserved price
ManualBidAmountInputRetryBidBannerEditable bid field with live "x of y will be retried" estimate
RetryConfirmButtonRetryBidBannerSubmits a manual retry; disabled after 3 total attempts used

UI States:

StateDescription
EmptyNo failed-low-bid recipients → banner not shown
LoadingSpinner on retry button while resend is in flight
ErrorRetry API call fails (system error, not bid-too-low) → inline error, no retry attempt consumed
SuccessRecipient status updates to "Retried" with icon shown in the recipient list

Figma: Pending — see Stitch Prompts section


Feature: Bid Information & Performance Insight (Campaign Detail)

FieldDetail
URL/campaigns/{id}/detail
AccessCampaign Admin (read/write), other roles (read-only) — Type:Campaign, Category:Marketing, MM API companies only

Component Tree:

ComponentParentPurpose
BidInformationPanelCampaignDetailPageShows strategy used, price, retry amount, retry-by user + timestamp
BidPerformanceInsightCampaignDetailPageShows total IDR saved vs. Default price
RetriedRecipientIconRecipientListRowPer-row indicator that a recipient was retried

UI States:

StateDescription
EmptyCampaign not yet sent → "Bid performance available once campaign starts sending"
LoadingSkeleton for both panels
ErrorSavings calculation fails → "—" shown with tooltip "Unable to calculate savings"
SuccessFull panel with computed values

Figma: Pending — see Stitch Prompts section

📊 UI State Diagram — Bid Strategy Selector

stateDiagram-v2
[*] --> Loading: Campaign Admin opens Bid Strategy step
Loading --> Empty: No historical price yet for recipient list
Loading --> Success: Historical price available
Loading --> Error: Price-fetch fails
Empty --> Success: Static ±10% defaults shown
Error --> Loading: Campaign Admin retries
Success --> [*]: Campaign Admin selects a strategy

Stitch UI Prompts

[Claude inserts the Stitch prompt table here at READY time. Leave blank until then.]

8. API & Webhook Behavior

#BehaviorEntity AffectedTriggered ByExpected BehaviorFailure Behavior
1Submit campaign with bid strategyCampaign + recipient listCampaign Admin selects a strategy and clicks Send now/Send laterBid strategy + bid amount stored on the campaign; applied to every recipient in the list; messages sent to Meta carrying the bid amountCustom amount outside [per-country published price, 2× that price] → blocked client-side with a validation message; logs bid_validation_failed
2Meta reports delivery + price outcome per messageMessageMeta's async delivered/read webhookReserved price recorded against the message (near-real-time); used for Bid Performance Insight and WA Balance deductionConfirmed: failed messages return no billable price from Meta — consistent with the no-charge-on-failure model. The webhook-reported price is indicative; Meta's monthly invoice is the source of truth for final billing (data-processing variance, same as Meta's Analytics API) — see Section 16 Risk.
3Retry failed messages (bid too low)Failed recipient subset of a campaignSystem detects bid < recipient's last known reserved price (Meta provides no dedicated error code for this failure)System automatically retries once using the system-recommended (highest known) price. If it fails again, Campaign Admin can manually retry up to 2 more times with a new bid amount (max 3 total attempts), each showing a live "x of y will be retried" estimateRetry count already at 3 → retry disabled, recipient marked permanently failed for this campaign. Still bid < reserved price after any attempt → fails again, consumes one attempt.
4Deduct WA Balance per delivered/read messageAccount WA BalanceMeta confirms delivered/read with a reserved priceBalance deducted by the webhook-reported reserved price, not the bid amount; reconciled monthly against Meta's invoiceDiscrepancy vs. monthly invoice beyond tolerance (>5%/day) → reconciliation adjustment applied, logged as wa_balance_reconciliation_mismatch. New deduction flow for Pending/Sent status is pending Bifrost's 1-pager (Section 14, blocking) — until live, deduction continues on the current at-delivery model.

Claude resolves during RFC: HTTP method, path, request/response JSON schema, error codes.

📊 API Sequence — Bid Submission & Reserved-Price Deduction

sequenceDiagram
participant CA as Campaign Admin
participant Q as Qontak System
participant M as Meta MM API

CA->>Q: Select bid strategy + amount, click Send
Q->>Q: Validate bid amount (per-country min/max)
Q->>M: Dispatch messages with bid amount per recipient
M-->>Q: Delivered/read webhook + reserved price
Q->>Q: Deduct WA Balance by reserved price
Note over Q: Reconcile vs. Meta invoice (monthly)
M-->>Q: Failed (bid < reserved price), no price returned
Q->>Q: Infer bid-too-low locally (no Meta error code)
Q->>M: Auto-retry once at recommended price
alt Still fails
Q-->>CA: Show retry banner, allow up to 2 manual retries
end

9. System Flow + User Stories + ACs

9.1. System Flow

Flow: Campaign Admin Sets Bid Strategy and Sends WA Marketing Campaign Type: User Journey + API Sequence

  1. Campaign Admin creates a WA marketing campaign (Send now/Send later) on an MM API company and selects a recipient list.
  2. System shows 4 bid strategy options (Cost Saver, Default, Aggressive, Custom), each with an indicative price.
  3. Campaign Admin selects a strategy or enters a Custom amount (bounded: per-country published price → 2× that price).
  4. System validates and stores the bid amount, applied uniformly to all recipients.
  5. Campaign Admin sends; system dispatches messages to Meta's MM API carrying the bid amount.
  6. Per recipient, Meta evaluates bid ≥ reserved price → delivered, charged the reserved price (reported via delivered/read webhook).
  7. Failure branch: bid < reserved price → not delivered, no charge; system infers this locally (no Meta error code exists) by comparing bid to the recipient's last known reserved price.
  8. System automatically retries the failed recipient once, using the system-recommended (highest known) price.
  9. Failure branch: If still failing, Campaign Detail shows a retry banner; Campaign Admin can manually retry up to 2 more times with an adjusted bid (max 3 attempts total).
  10. On each successful delivery, WA Balance is deducted by the webhook-reported reserved price; reconciled monthly against Meta's invoice.
  11. Campaign Detail surfaces Bid Information, Bid Performance Insight (IDR saved), and a retried-recipient icon.
  12. Failure branch: All 3 attempts exhausted and still failing → recipient marked permanently failed for this campaign, no further retry.

📊 System Flow — WA Campaign Bid Strategy & Delivery

sequenceDiagram
participant CA as Campaign Admin
participant Q as Qontak System
participant M as Meta MM API

CA->>Q: Create campaign, select recipient list
Q->>CA: Show 4 bid strategies with indicative prices
CA->>Q: Select strategy / enter Custom amount
Q->>Q: Validate bid (per-country min/max)
CA->>Q: Click Send
Q->>M: Dispatch messages with bid amount
alt bid >= reserved price
M-->>Q: Delivered/read webhook + reserved price
Q->>Q: Deduct WA Balance by reserved price
Q->>CA: Show Bid Information + Performance Insight
else bid < reserved price
M-->>Q: Not delivered, no charge
Q->>Q: Infer bid-too-low locally (no Meta error code)
Q->>M: Auto-retry once at recommended price
alt Auto-retry still fails
Q->>CA: Show retry banner (up to 2 manual retries)
loop up to 2 manual retries
CA->>Q: Submit new bid amount
Q->>M: Resend with new bid
end
alt All 3 attempts exhausted
Q->>CA: Mark recipient permanently failed
end
end
end

9.2. User Stories

User StoryImportanceMockupTechnical NotesAcceptance Criteria
[WABID-S01] — Select Bid Strategy for Campaign

As a Campaign Admin, I want to select a bid strategy (Cost Saver/Default/Aggressive/Custom) when creating a WA marketing campaign, so that I control how much I'm willing to pay to reach each customer.
Must HavePending — see Stitch PromptsData Fields:
strategy (enum, required) — User input
bid_amount (decimal, required if custom) — User input
company_id (Auth session)
country (Account settings — determines price floor/ceiling)

Before-After Behavior: Campaigns previously always used Meta's fixed per-message price with no user control; after this story, Campaign Admins choose from 4 bid strategies with a mandatory bid amount.
— Happy Path —
• AC-1: Given a Campaign Admin creating a campaign on an MM API company, when they open campaign creation, then they see 4 strategies each with an indicative price.
• AC-2: Given they select Custom, when they enter an amount within [per-country published price, 2× that price], then it's accepted and stored.
• AC-3: Given they don't change the strategy, when they proceed to send, then "Default" is applied automatically with its indicative price.
• AC-4: Given a strategy is already selected, when they change it before clicking Send, then the new choice replaces the old one with no restriction.

— Error / Unhappy Path —
• ERR-1: Given Custom is selected, when the amount is outside the valid range, then submission is blocked with a validation message showing the valid range, and logs bid_validation_failed.

— Permission Model —
• CAN: Campaign Admin on MM API companies
• CANNOT: Campaign Admin on Cloud-API-only companies; read-only roles
• Unauthorized: Bid Strategy step not rendered; falls back to Meta's default fixed pricing

— UI States —
• Loading: Skeleton while historical price data loads
• Empty: No historical price yet → Cost Saver/Aggressive show static ±10% defaults
• Error: Price-fetch fails → only Default shown, others disabled with tooltip
• Success: All 4 strategies shown with computed prices

— Negative Scenarios — (from Non-Goals)
• NEG-1: Given a company on Cloud API (not MM API), when they create a campaign, then no bid strategy step is shown and Non-Default pricing does not apply.
• NEG-2: Given a Campaign Admin wants a per-recipient bid, when they use the recipient list, then only a single uniform bid amount applies to the whole list.
[WABID-S02] — Retry Failed Messages with Adjusted Bid

As a Campaign Admin, I want to retry messages that failed because my bid was too low, so that I can still reach those customers.
Must HavePending — see Stitch PromptsData Fields:
recipient_id, retry_count (0-3)
new_bid_amount (decimal, manual retries) — User input
failure_reason (derived locally — see Section 16 Risk on no Meta error code)

Before-After Behavior: Previously, failed recipients had no recovery path; after this story, 1 automatic retry fires, then up to 2 manual retries with a new bid (3 total). Note: once submitted, a retry cannot be cancelled — the only "undo" is that failed sends incur no charge (WABID-S04 AC-2).
— Happy Path —
• AC-1: Given a message fails (bid < last known reserved price), when detected, then system auto-retries once using the recommended (highest known) price, retry_count→1.
• AC-2: Given the auto-retry also fails, when Campaign Admin opens Campaign Detail, then they see a retry banner "Message fails to deliver. You need at least Rp xx to reach this customer," editable bid field, and a live "x of y will be retried" estimate.
• AC-3: Given a manual retry is submitted and retry_count < 3, then message resends and count increments; at count=3 and still failing, recipient is marked permanently failed.

— Error / Unhappy Path —
• ERR-1: Given retry_count = 3, when another retry is attempted, then the control is disabled with tooltip "Maximum retries reached," no API call made.
• ERR-2: Given a manual retry bid is still below the per-country minimum, then it's blocked (same validation as WABID-S01 ERR-1).

— Permission Model —
• CAN: Campaign Admin who owns the campaign, MM API companies
• CANNOT: Cloud-API companies; read-only roles
• Unauthorized: Retry controls not rendered

— UI States —
• Loading: Spinner on retry button
• Empty: No failed-low-bid recipients → no banner
• Error: Retry API call fails (system error, not bid-too-low) → inline error, no attempt consumed
• Success: Recipient status → "Retried" with icon

— Negative Scenarios — (from Non-Goals)
• NEG-1: Given a recipient has exhausted all 3 attempts, when the Campaign Admin tries to retry again, then no further retry is offered this campaign.
• NEG-2: Given a "Send recurring" campaign scheduled before this feature's release, when a recipient fails, then no retry is offered.
[WABID-S03] — View Bid Information and Performance Insight

As a Campaign Admin, I want to see bid strategy, retry history, and cost savings on Campaign Detail, so that I can evaluate whether my bidding strategy is working.
Should HavePending — see Stitch PromptsData Fields:
bid_strategy, bid_price, retry_amount (NULL if none), retried_by (user+timestamp)
saved_amount_idr = Σ(delivered/read) × (default_price − reserved_price)

Before-After Behavior: Campaign Detail previously showed no bid data; after this story, it surfaces bid strategy, retry history, and computed savings — Type:Campaign, Category:Marketing, MM API only.
— Happy Path —
• AC-1: Given a Type:Campaign/Category:Marketing campaign on MM API, when Campaign Admin opens Campaign Detail, then Bid Information (strategy, price, retry amount, retry-by) is shown.
• AC-2: Given ≥1 delivered/read message priced below Default, then Bid Performance Insight shows total IDR saved.
• AC-3: Given a recipient was retried, then a retried-recipient icon shows on that row.

— Error / Unhappy Path —
• ERR-1: Given the campaign is Cloud-API-only or not Type:Campaign/Category:Marketing, then the Bid Information panel is not rendered.

— Permission Model —
• CAN: Campaign Admin, read-only viewers (MM API companies)
• CANNOT: Roles on Cloud-API-only companies
• Unauthorized: Panel not rendered

— UI States —
• Loading: Skeleton for both blocks
• Empty: Campaign not yet sent → "Bid performance available once campaign starts sending"
• Error: Savings calc fails → "—" with tooltip
• Success: Full panel with computed values

— Negative Scenarios — (from Non-Goals)
• NEG-1: Given a Cloud API company, when they open any Campaign Detail, then no Bid Information panel is rendered.
[WABID-S04] — WhatsApp Balance Deducted by Reserved Price

As a Campaign Admin, I want my WhatsApp Balance charged the actual reserved price, not my bid, so that I don't overpay for messages that cost less than my bid.
Must HaveN/A — backend/billingData Fields:
reserved_price (webhook), invoice_price (Meta monthly invoice)
account_wa_balance

Before-After Behavior: Today, balance is deducted a fixed per-message price; after this story, it's deducted by the webhook-reported reserved price, reconciled monthly vs. Meta's invoice. Depends on Bifrost's new deduction flow (Section 14, blocking).
— Happy Path —
• AC-1: Given a message is delivered/read, when Meta's webhook reports the reserved price, then WA Balance is deducted by that price (not the bid).
• AC-2: Given a message fails (bid < reserved price), then no deduction occurs.
• AC-3: Given a month of deductions, when Meta's invoice arrives, then system reconciles total deducted vs. invoiced and flags discrepancy >5%/day (Observability Alert 2).

— Error / Unhappy Path —
• ERR-1: Given webhook price is later inconsistent with the invoice, then logs wa_balance_reconciliation_mismatch and applies a balance adjustment.
• ERR-2: Given Bifrost's new deduction flow isn't live yet, then deduction continues on the current at-delivery model until it ships (Section 14 dependency, blocking).

— Permission Model —
• CAN: System-triggered; Finance/Billing admin can view balance history
• CANNOT: Campaign Admin cannot directly adjust deductions
• Unauthorized: N/A — backend process

— UI States —
• N/A — reflected read-only in WABID-S03's panels

— Negative Scenarios — (from Non-Goals)
• NEG-1: Given a failed message, when balance deduction runs, then no amount is ever deducted for that message.

Dependencies: WABID-S02 → WABID-S01; WABID-S03 → WABID-S01, WABID-S02; WABID-S04 → WABID-S01, Bifrost WA Balance deduction flow (blocking)

10. Rollout

FieldValue
Feature flagwa_dynamic_pricing_bid_strategy — default: OFF (see Constraints)
Stage 1Internal QA accounts only (Qontak internal test accounts) — validate bid strategy math + WA Balance deduction against Meta's staging/sandbox pricing
Stage 2Closed beta — ~5 high-volume MM API accounts, manually enabled, monitored closely for balance-deduction accuracy
Stage 3All MM API accounts on request
GAAll MM API accounts (Cloud API accounts excluded — see Non-Goals)
Backward compatYes — accounts default to "Default" strategy (matches current fixed per-message pricing) until they opt into a different strategy
MigrationNone required for existing campaigns; only new campaigns after the flag is ON carry a bid strategy field

11. Observability

Key Events:

Event NameTriggerProperties
bid_strategy_selectedCampaign Admin picks a strategy on campaign creationcampaign_id, strategy, bid_amount
campaign_sent_with_bidCampaign dispatched to Meta with a bid amountcampaign_id, recipient_count, bid_amount, strategy
message_delivered_pricedMeta confirms delivery/read with reserved pricemessage_id, campaign_id, reserved_price, bid_amount
message_failed_low_bidSystem infers a message failed due to bid < reserved pricemessage_id, campaign_id, bid_amount
bid_retry_submittedAutomatic or manual retry submittedcampaign_id, new_bid_amount, retry_count, retry_type (automatic/manual), estimated_success_pct
wa_balance_deductedBalance deducted for a delivered/read messageaccount_id, message_id, reserved_price
wa_balance_reconciliation_mismatchMonthly invoice reconciliation finds a discrepancyaccount_id, deducted_total, invoiced_total, variance_pct
FieldDetail
Dashboard ownerRevenue Squad
Alert 1message_failed_low_bid rate > 20% of sent in 1 hour → Slack #revenue-alerts
Alert 2wa_balance_deducted sum diverges from Meta's reported charges by > 5% in a day → PagerDuty on-call (billing accuracy risk)

11.1. Post-Launch Monitoring Cadence

FieldDetail
Review cadenceWeekly for the first 4 weeks post-GA, then monthly
OwnerRevenue Squad PM (Evelin Suwantio)
Review scopemessage_failed_low_bid, bid_retry_submitted, wa_balance_deducted, wa_balance_reconciliation_mismatch
Trigger threshold 1message_failed_low_bid rate rises > 15% week-over-week → investigate strategy defaults immediately
Trigger threshold 2wa_balance_reconciliation_mismatch variance > 5% for 2 consecutive months → escalate to Bifrost + Finance
Rollback considerationIf reconciliation variance exceeds 5% and cannot be resolved within 5 business days, PM disables wa_dynamic_pricing_bid_strategy globally pending root cause.

12. Success Metrics

Adoption & Usage:

MetricDefinitionBaselineTarget
Non-Default strategy adoption% of MM API campaigns using Cost Saver/Aggressive/Custom (not Default)N/A — new feature≥25% of MM API campaigns within 30 days of GA
Retry usage rate% of campaigns with low-bid failures that use the retry flowN/A — new feature≥40% within 30 days of GA

Quality & Accuracy:

MetricDefinitionBaselineTarget
Delivery rate parityDelivered ÷ sent, vs. current fixed-price delivery rateN/A — unmeasurable todayWithin ±2% of current baseline within 60 days of GA
WA Balance deduction accuracyReserved-price deductions matching Meta's reported chargesN/A — new mechanism≥99.5% accuracy within 30 days

Efficiency & Impact:

MetricDefinitionBaselineTarget
Avg. cost saved per messageIDR saved via Cost Saver strategy vs. Default priceN/A — new featureReport avg. savings ≥Rp30/message within 60 days
"Missing delivery" support ticketsVolume of tickets citing undelivered WA campaign messagesN/A — unmeasurable today≤50% reduction within 90 days of GA

13. Launch Plan & Stage Gates

StageAudienceDurationSuccess Gate to AdvanceOwner
Internal AlphaInternal QA accounts1 week0 P0/P1 bugs; WA Balance deduction accuracy ≥99.5% in test trafficPM + QA
Closed Beta~5 high-volume MM API accounts2 weeksDelivery rate parity within ±2% of baseline; 0 balance-deduction discrepancy incidentsPM + CSM
Open BetaAll MM API accounts on request3 weeksmessage_failed_low_bid rate stable (not trending up week-over-week); retry usage ≥20%Eng Lead
GAAll MM API accountsOngoingAll Open Beta gates sustained 2 weeks; WA Balance deduction accuracy ≥99.5% sustainedPM + PMM

14. Dependencies

DependencyOwning TeamDeliverable NeededBlocking?
WA Balance deduction flow supporting Reserved Price (for Pending/Sent status)Bifrost Squad (Addo Hernando)New balance deduction flow — BIF PRD/1-PagerYES
Meta dynamic pricing API contract confirmedMeta (external vendor)Answers to the open questions on reserved-price timing, price-on-failure, price volatility (Section 16)YES

15. Key Decisions + Alternatives Rejected

Decisions Made

DateDecisionRationale
2026-06-05Offer 4 preset bid strategies (Cost Saver, Default, Aggressive, Custom) instead of requiring manual per-campaign biddingReduces user education burden (Business Goal); most users lack visibility into per-recipient demand to bid manually
2026-06-05Charge the actual Reserved Price, not the user's Bid AmountMatches Meta's billing model; avoids overcharging when bid > reserved price
2026-07-03Retry model: 1 automatic retry, then up to 2 manual retries (by changing bid amount), max 3 attempts totalBalances recovery opportunity against spam/cost risk while giving users enough control to succeed via bid adjustment
2026-07-03Bid amount bounds derived from Meta's published per-country marketing-message price (min = published price, max = 2× published price), not a fixed IDR valueReflects Meta's actual per-country pricing rather than misprice non-Indonesia markets with a fixed Rp300/Rp1,192.66 bound

Alternatives Rejected

AlternativeWhy RejectedDate
Manual per-recipient bid entryToo much operational burden; users can't see per-recipient reserved-price demand2026-06-05
Unlimited retries on low-bid failureSpam/cost risk to recipients and account balance2026-06-05
Custom-only bidding (no presets)Doesn't reduce user education burden — defeats the stated Business Goal2026-06-05
Fixed universal min/max bid regardless of countryDoesn't reflect Meta's per-country published pricing; would misprice for non-Indonesia markets2026-07-03

16. Open Questions

#TypeQuestion / FactOwnerDeadline / Mitigation
1AssumptionDynamic pricing applies to MM API only (Cloud API excluded)Evelin SuwantioConfirmed 2026-07-03
2AssumptionBid amount is mandatory before sending; template campaigns default to a system value if not explicitly chosenEvelin SuwantioConfirmed 2026-07-03
3RiskDelivered/read webhook price may differ from the final invoice due to data-processing variance (same as Meta's Analytics API)Burhanudin HakimMitigation: deduct near-real-time from webhook price; reconcile monthly against Meta's invoice (invoice = source of truth). Covered by Observability Alert 2 and event wa_balance_reconciliation_mismatch.
4AssumptionMeta's reserved price is influenced by message volume/engagement history (exact formula proprietary, not fully replicable)Burhanudin HakimConfirmed 2026-07-03
5AssumptionFinal confirmed price charged is only available via Meta's monthly invoice (not guaranteed real-time per-message)Burhanudin HakimConfirmed 2026-07-03 — ties to Risk #3
6RiskMeta provides no error code for "failed — bid too low," so the system cannot directly detect this failure reason from Meta's responseBurhanudin HakimMitigation: infer bid-too-low locally by comparing the submitted bid against the recipient's last known reserved price; needs engineering validation before RFC.
7AssumptionFailed messages return no billable price info from Meta (consistent with no-charge-on-failure)Burhanudin HakimConfirmed 2026-07-03
8AssumptionRetry model corrected: 1 automatic retry, then up to 2 manual retries (changing bid each time) = max 3 attempts totalEvelin SuwantioConfirmed 2026-07-03 — supersedes the source doc's original "max 1 retry"
9AssumptionNo universal minimum bid — floor/ceiling derived from Meta's published per-country marketing price (Rp300 is Indonesia's value specifically)Evelin SuwantioConfirmed 2026-07-03 — corrects Section 5 Constraints

Types: Assumption · Open Question · Risk

PRD CHANGELOG

VersionDateBySectionTypeSummary
1.02026-07-03ClaudeAllREFORMATTEDReformatted from the legacy Confluence draft to Qontak PRD template v1.2. 9 sections extracted/mapped, 6 sections filled via coaching interview (S6, S7, S9-S13 gaps), 0 flags remaining — all residual Meta-API unknowns tracked as mitigated Risk rows in Section 16.